計画性のないテストによって、テストの品質が下がる
ソフトウェアテストでは、テスト計画が重要である。テストケースの作成やテストの実施は、すべてテスト計画に従って行われる。テスト計画を十分に練ることによって、テストの品質を高めることができる。
テストファーストでは、このテスト計画がおざなりになりがちだ。実際、雑誌などの紹介記事では、いきなり具体的なテストケースをコードとして書き始めている事例が多い。
このやり方は、テスト計画が 「テスト項目のレビューは、項目を挙げる前に行おう」 で示した、いきなりテスト項目の一覧表を作ってしまうケースと、同じ問題を抱えている。つまり、テストが正しいかどうか、レビューで確認できない。その結果、テストの品質を高く保つことが難しくなる。
テスト計画では、テストケースの優先度の評価も重要である。限られた工数の中で品質を高めるためには、リスクの高いものから順にテストを行う、 リスク・ベース・テスト の考えが有効だ。
しかし、テスト計画を立てずに、いきなりテストケースを挙げていく方法では、簡単で作りやすいテストケースから書いてしまいがちである。だが、簡単なテストケースは、処理の実装も簡単であり、バグが発生する可能性は低い。つまり、あまり問題の無さそうなテストケースほど優先して作っていることになる。これは、 リスク・ベース・テスト の考えに反するものだ。
なお、この弊害に対する解決策として、 JUnit Primer では、次のようなイディオムが提示されている。
- Write tests for the areas of code with the highest probability of breakage.
- Write tests that have the highest possible return on your testing investment.
単体テストが行われず、バグが見逃される
従来のソフトウェア開発では、コーディングの後で、ある程度の工数をかけて、単体テストが行われる。
テストファーストは、コーディングの工程の中に、単体テストが組み込まれたものだ。コーディングが完了した時点では、テストケースはすべてパスしている。そのため、コーディング完了と単体テスト完了がイコールだと見なされる。
だが、 「バグを見つけるためのテストをしよう」 で述べたように、テストには、確認のためのテストと、バグを見つけるためのテストの、2つのアプローチがある。
テストファーストの考えは、作るべきモノの仕様を先にテストケースとして書き表すことで、仕様から外れないように導く、というものだ。ここで、テストケースに書かれるテストとは、確認のためのテストである。つまり、ある条件である処理を行った時に、どのような結果になるか、期待される結果が明記できるようなテストだ。
一方、バグを見つけるためのテストは、テストを実行した際に期待される結果を、予め明記できない。そのため、テストファーストでは、このアプローチのテストケースを作ることはほとんど無い。つまり、テストファーストでは、実施できるテストは限られており、それだけでは、ソフトウェアの品質を保つためには不十分である。
効果的でないテストで工数を浪費する
テストファーストでは、テストケースをコードとして書く必要がある。コーディング量が増える分だけ、どうしても工数は増えてしまう、という懸念は、以前から指摘されている。だが、テストファーストが引き起こす工数の増大は、それだけではない。
技術者の中には、完全なテスト項目を作り上げ、完全なテストを実施し、完全なテストの記録を残したい、という考えの人も少なくない。それでも、従来のソフトウェア開発では、それまでにかかった工数や、締め切り、ソフトウェアの品質、チームの状況などにより、現実的なテストに落ち着くことが多い。
テストファーストでは、テストケースの作成が、一番初めの作業になる。ソースコードが存在しないため、どれくらいの規模のテストを実施すべきか判断する材料が少ない。また、スケジュールにも余裕がある。この状況では、完全なテストを行いたい技術者は、必要以上にテストケースの作成に時間をかけてしまう、テスト中毒に陥りやすい。
しかも、これまで述べてきたように、こうして作られたテストケースは、あまり問題の無さそうな、作りやすいものばかりになる危険性が高い。工数が増えても効果は薄く、工数を浪費しただけに終わりがちである。
また、テストファーストで実施するテストは、事実上、単体テストに過ぎない。結合テスト・システムテストは、従来どおり行わなくてはならない。
単体テストの工数が増大しても、結合テスト・システムテストに十分な工数をかけるのであれば、問題はない。だが、テスト全体の工数として、従来の開発手法と同じ時間しか見積もっていなければ、テストファーストによって単体テストの工数が増えた分、結合テスト・システムテストを軽視することになる。
テストファーストでは、テストケースの作成に必要以上に時間をかけやすい。これは、テストファーストを導入すれば、自然に、単体テストにかける比重が大きくなることを意味する。
従来、単体テストの不備で問題が多く発生していたのであれば、テストファーストは最適な解決策であろう。だが、それ以外の要因で問題が起きていたのであれば、テストファーストの導入によって、的外れなところに力を注ぐことになり、より事態が悪化することにもなりかねない。
読みづらいドキュメントが、修正を妨げる
テストファーストの利点の1つに、仕様がテストケースという形で明確に記述される、という点がある。このテストケースは、仕様を明文化したドキュメントとしての役割も果たす、と言われている。
だが、テストファーストでは、テストケースはソースコードという形式で表現される。これは、果たしてドキュメントとして適切な表現形式であろうか。
従来のソフトウェアテストでは、テスト計画書は日本語や英語の文章で書かれていた。そこには、テストの意図や重要性、テストケースを挙げる方針や基準などが書かれていた。それらを、ソースコードという表現形式から、果たして読み取れるだろうか。
テストケースにドキュメントとしての役割を期待するのであれば、可読性が重要となる。安易にテストケースを書き連ねていくのではなく、コードから仕様を読み取れるように、分かりやすい書き方をすることが非常に重要となる。
これは、仕様変更や機能追加の際に、大きな問題となる。将来の担当者が、どのテストケースを変更して良いものか、理解できなくてはならない。
さらに、テストファーストによる開発では、リファクタリングが必要になることが多い。リファクタリングをした後では、一部のテストケースはエラーになるだろう。この時、エラーとなったテストケースについて、それが従来通りパスすべきものか、それともリファクタリングによってテストそのものを変更する必要が生じたのかを、判断できなくてはならない。
現在では、ソースコードの可読性はたいへん重要視されている。だが、どれほど分かりやすくソースコードを書いたとしても、日本語や英語で書かれたコメントや文書での説明が必要になる場合がある。
テストケースのコードについても同様であろう。どれほど分かりやすく書いたとしても、ソースコードという表現形式では、どうしても伝えられないことも出てくるのではないか。
なお、この弊害に対する解決策として、 JUnit best practices では、次のようなプラクティスが提示されている。
- Name tests properly
- Document tests in javadoc
また、JUnit スーパーTips(JavaWorld 2005 October)では、クラスやメソッド、アサーションの名前に日本語を使って、内容を分かりやすくする、という方法が提案されている。
テストファーストの流行は、ソフトウェアの品質に対する意識が高まっていることの表れであろう。だが、どのような手法も、ツールも、あくまでテストを行うための「道具」の1つにすぎない。ソフトウェアの品質は、結局、これらの道具を人間がいかに使いこなすか、にかかっているのだ。