はじめに
プログラマの中には、TDDのような自動テストを整備すれば、手動テストは必要なくなると考えている方もいるようです。本記事では、主にプログラマー向けに、手動テストの大切さとはじめ方を書きます。
はじめ方に忍者式テストが出てきます。
プログラマーが得意なテスト、不得意なテスト
プログラマーはCheckingが得意です。Testingは不得意です。
テストには Testing と Checking の二つの作業がある
Michael Boltonという人のお言葉があります。
Testing vs. Checking « Developsense Blog
Checking Is Confirmation
Testing Is Exploration and Learning
テストにという行為はCheckingとTestingの二つの行為の分けられます。
Checkingは既知の不具合が起きていないか確認する作業です。
Testingは未知の不具合を探す作業です。
プログラマーが得意なテスト(Checking)
プログラマーは、「xUnitを使った自動テスト用」(以下、自動テスト)のテストコードを書くことが得意です。
今、得意でないプログラマーもTDDという優れた導入方法が確立されています。TDDから始めれば、自動テストを書けるようになるでしょう。
自動テストはCheking向き
Checkingは確認内容が分かっているので、自動テストはに向いています。
また、Checkingを自動化すると、実行回数が増やせます。
実行回数を増やすことで、早めに不具合が検知できます。
一方で未知の不具合を自動テストで発見するのは難しいです。
テストの手順を変える工数が手動テストに比べて大きいためです。
パラメータの変更であれば簡単ですが、順序の変更は大変です。
脱線
Googleは、テストケースを自動生成する手法を研究・実践しているようです。
対象がAPIであればTDDで、呼び出しパターンを変更しながら開発することで、インタフェース設計の不具合を見つけるができます。手動テストでは、API以外のアプリケーションのユーザインタフェースに対し、TDDと同様に不具合を探すことができます。
プログラマーが不得意なテスト(Testing)
例えば、見た目のテスト
例えば、次の画像ではモーダルダイアログのタイトルが見切れています。
Safariでのみ発生し、Google Chromeでは発生しませんでした。
このような不具合を自動テストで発見することはとても難しいです。
なぜでしょうか?
プログラマーが予想できる不具合
プログラマーが自動テストを書くとき、プログラマーが予想する不具合しかテストすることができません。前述のテストケースの場合、ブラウザによって、デフォルトのフォントやpaddiingサイズが異なることを知らなければ、思いつきません。
プログラマーは不具合のパターンを学習することで、不具合を確認しやすいテストケースを書けるようになります。
見た目の自動テストはコストが高い
見た目の自動テストはxUnitだけでは実装できません。SeleniumやNightmareを使ったり、スクリーンショットのdiffを取ったりすれば、自動テストとして実装することは可能です。
また、見た目は変更されやすいため、テストコードのメンテナンス回数が多くなる点も軽視できません。
できれば、難しい自動テストの実装や手間のかかるメンテナンスより、プロダクトコードへの機能追加や改善に力を注ぎたいところです。
自分が書いたプログラムには不具合がないと思いたい
プログラマーは「自分の書いたプログラムには不具合がない」という先入観を持ちがちです。見た目や振る舞いに違和感があっても、機能を満たしていれば「不具合ではない」と判断しがちです。
参考:87-プログラマとテスターが協力してできること - やさしいデスマーチ
まとめ
- プログラマーはあらかじめ知っている不具合しか見つけられない
- 見た目の自動テストはコストが高いので書きたくない
- 自分のプログラムに不具合があるとは思いたくない
プログラマーはテストで未知の不具合を見つけるのが苦手です。
Testing 予想していない不具合を見つける方法
苦手であっても、未知の不具合も見つけたいです。ここでは、そのために役立つ手動テストの効果を紹介します。
手動テストでは、次の手順で不具合を探します。
- テスト項目にしたがって、手でアプリケーションを動かす
- 確認項目と不一致がないか確認する
ここまではCheckingです。
実際に手でテストをしていると、確認項目には載っていない点に引っ掛かりを覚えることがあります。
この違和感が未知の不具合であることがあります。
なぜでしょうか?
違和感から不具合を見つける
違和感は期待することと実際の差です。
期待することは、人によって変わります。
プログラマーとユーザーでは、アプリケーションに期待することが違います。
プログラマーの視点で、アプリケーションを見ると、プログラマーの違和感が見つかります。
- 機能を満たしているか
- プログラマーの想像したモデルを表現しているか
ユーザーの視点で、アプリケーションを見ると、違った違和感が見つかります。
- 一見して欲しい情報がわかりやすいか
- ボタンの配置が使いやすいか
- ユーザーの欲しかったモデルが表現されているか
異なる立場の人の違和感を同じように感じることは難しいです。
事前に作る確認項目は、想像上のユーザーが想像上のアプリケーションに感じる違和感を元にして作ります。
プログラマーはユーザーではないので、実際のユーザーが抱く違和感を感じることはできません。
また、ユーザーも想像上のアプリケーションを使った違和感を想像するだけで、実際のアプリケーションを使った際の違和感を、事前に感じることはできません。
手動テストを繰り返す
手動テストを繰り返すと、アプリケーションと、アプリケーションにユーザーが期待することへの理解が進みます。ユーザーの期待することを想像できるようになります。プログラマーの視点だけではわからなかった不具合がみつかるようになります。
特に繰り返し行うことが重要です。受託開発などでは、納品時に一回だけ行うことが多いです。しかし、一回だけの手動ではアプリケーションへの理解が進みません。
Testing Is Exploration and Learning
Testing とは探索であり学習です。
手動テストを繰り返せば、プログラマーもTestingできます!
手動テストを始める条件
前提:エンドツーエンドテストを相手にする
ここでは手動テストが対象とするのは、エンドツーエンドテストとします。
- ライブラリのテストはxUnitで簡単にテストコードが書ける
- Web APIもClientの戻り値をxUnitで検証するテストコードが書きやすい
- エンドツーエンドだとSeleniumやNightmareと創意工夫が必要
CI/CDがないと繰り返しテストできない
手動テストを繰り返し行うには、Continuous IntegrationやContinuous Delivery(以下、CI/CD)の環境が整っている必要があります。CI/CDの環境がないと、テストのたびに環境構築が必要です。
プログラマーが割と得意な領域なので、頑張って用意しましょう。
また、アプリケーション固有の課題が多いので、一般的なお話は難しいです。
テストケースを作ろう
テストケースは文章として残すと良いです。
テストケースがあれば、記憶に頼らずに繰り返せる。テストのたびに仕様を思い出すコストが減らせる。テストの実行コストが減ります。
テストケースがあれば、複数の人がテストできる。いろんな人がテストに参加できる。テストする人が増えると新しい期待が得られます。
テストケースがあれば、正しい動作を記録に残せる。久しぶりに修正する機能の変更前の動作がわかる。変更に着手するハードルが下がります。
そして、良いテストケースが増えるとテストへの自信が増します。
テストケースの育て方
最初から完璧なテストケースを揃えるのは無理です。最初は少数の不完全なテストケースからはじめます。徐々にテストケースの完成度をあげ、徐々にテストケースを増やしていきます。
テストケースを増やしていくには、忍者式テストを使います。
代表的な正常系操作から始める
最初は代表的な正常系操作をテストケースとします。
記述の詳細さはテストする人がわかれば良いレベルです。
あまり厳密に書く必要はありません。
件数は、最少で1件でも構いません。
例えば、次のような粒度でテストケースを書いています。
- https://github.com/pubannotation/textae/wiki/userAcceptanceTest
- https://github.com/lodqa/grapheditor/wiki/User-Acceptance-Test
毎日新しいテストケースを追加する
新しく追加した機能など、気になる機能のテストケースを追加します。
これも最初は正常系だけで大丈夫です。
気になってきたら異常系も追加しましょう。
毎日テストする
用意したテストケースは毎日実行しましょう。
テストしながら、テストのやり方に迷う表現があったら、実行しながらテストケースを直しましょう。
毎日触るとちょっと違うことをしてしまう
毎日実行すると、元のテストケースとはちょっと違ったことをしたくなります。
その時は、そのままちょっと違うことをしましょう。
秋山 浩一さんのお言葉
https://twitter.com/akiyama924/status/506255401537384448
テスターは、いい加減なテストケースを元に、そこからちょっと外れた操作をしてバグを見つけていると思います。
本職のテスターの方も同じことをしています。
違ったことをして不具合が見つかったらテストケースにする
不具合が見つかるテストケースは良いテストケースです。
新しいテストケースとして追加して、記録しましょう。
増えすぎたテストケースの減らし方
テストケースが増えてくるとテスト実行時間が伸びてきます。
プログラマーはテストが苦手なので、テストの実行時間が1時間を超えると、違和感を見つけられなくなります。
育ちすぎたテストケースは毎日はやらないリストに移動して、日々のテストケースを減らしましょう。
テストを減らす時の判断材料はいくつかあります。
面倒臭いテストを減らす
面倒臭いテストを減らします。
面倒臭いテストは手間の割に不具合が見つからないから面倒臭いのです。
効率の悪いテストなので優先的に減らしましょう。
面倒臭い感覚を正しく捉えるためにテストは健康な状態でやらなければなりません。
疲れている状態でテストすると、すべてのテストケースが面倒臭く感じます。
どのテストケースが、よいテストケースかわからなくなります。
テストケースの蘇らせ方
テストを減らしていると不安になってきます。減らしたテストはいつまでやらなくていいのでしょうか?
テストを蘇らせる方法もいくつかあります。
変更した機能に関わるテストケースを蘇らせる
新しく追加した機能に関わっていそうなテストケースを蘇らせます。回帰テストの効果を狙っています。
副次的に関連のある機能を触ることで、機能間で矛盾した点、例えばボタンや入力欄の配置の不統一、を発見することにもつながります。もちろん修正前に、デザインは検討しますが、意外に忘れています。
こうして不具合が見つかることで、テストケースに対する信頼感がまします。
この手法には多少の問題があります。関わっていそうなテストケースに見落としがあることがあります。機能が増えてくると見落としが増えます。
また、プロダクトコードがある程度育つと、小さい変更だと壊れなくなってきます。
頭を使って関わっていそうなテストケースを探した割に、不具合が見つからなくなります。
非効率な作業を続けるとテストケースに対する信頼感が下がります
最終実施日で自動ローテーション
そこで、機械的なルールで、過去のテストケースを蘇らせます。
毎日するテストケースの一番過去に追加したテストケース、つまり実施回数が一番多いテストケースを毎日はやらないテストケースに移動します。
代わりに同数の毎日はやらないテストケースに一番過去に追加したテストケース、つまり最終実施日が最も過去のテストケースを毎日するテストケースに移動します。
たとえば次のスクリプトでは12件ずつ入れ替えています。
https://github.com/ledsun/nukadoko
対象のテストケースを時系列降順にならべているため、後ろから12件ずつを入れ替えています。
また、機能変更に依存したテストケースの入れ替えでは、機能変更が無い日はテストケースも変更がありません。
毎日完全に同じテストケースを実行するのは辛いです。機械的に入れ替えることで、新鮮な気持ちでテストを実行できます。
おわりに
手動テストを繰り返せば、プログラマーもTestingできます!