|
ユニットテストに関する「うんちく」http://works.dgic.co.jp/djwiki/Viewpage.do?pid=@E383A6E3838BE38383E38388E38386E382B9E38388E381ABE996A2E38199E3828BE3808CE38186E38293E381A1E3818FE3808DEncoding:UTF-8 / Locale:en_US / PageStatus: Frozen Owner of this page:kataoka |
はじめにいまさらですが、我々は、単体テストを自動化して、繰り返し実行することには、とてもメリットがあると考えています。そして、いままで、たくさんの資料や文献を参考にして、Webアプリケーションのテストの自動化にチャレンジしてきました。 最初は、JUnit、dbUnitを使って、ビジネスロジックやDAO、その他のオブジェクトを一塊のロジックとしてとらえ、たくさんのassert文を書いてテストしていました。 それでも、十分な効果があったのですが、なにしろ検査の対象となるロジックが広範囲であるため、いろいろな問題があることがわかりました。 とくに問題だと思ったのは、
といったことがあげられます。 プロジェクト末期になると、プログラムの修正よりも、テストの修正のほうが多いという場合が、多々あり、そのコストは、結構なものになります。 最近では、これらを解決するため
ということを実践しています。 djUnitは、カバレッジレポートをもっと頻繁に見れるようにしたり、ひとつのクラスのテストに集中しやすくします。 「ひとつのクラスのテストに集中する」ために有効な仕組み、それが、 Virtual Mock Objects です。 ひとつのクラスのテストに集中する ΛJUnitによるテストは、対象オブジェクトのあるメソッドを実行し、実行後の状態を検証するというのが基本だと思います。 簡単に「ひとつのクラスのテスト」といっても、ほとんどの場合、クラスは他のクラスに依存していますので、 検査対象オブジェクトの、あるメソッドを実行すると、 といったように、複数のクラスのロジックが実行されてしまいます。 このロジックは最終的に、データベースやファイルにアクセスするかもしれませんし、他のマシンと通信するかもしれません。 ありそうなのは、 現在時刻を取得し、なんらかの処理を行う かもしれません。 これでは、他のクラスに依存するばかりか、データベースのデータや、ファイルの内容に依存していることになります。 とすると、テストを実行したときの「データの状態」「ファイルの状態」「時間帯」によって、実行後のオブジェクトの状態が変化してしまいます。 これでは、テストを実行するたびに結果が変化しますので、正しいテストとはいえないと思います。 こういった依存関係があるなかで、検査対象クラスだけに集中するには、MockObject(擬似オブジェクト)が役立つといわれています。 検査対象以外のオブジェクトを、MockObjectに差し替えて、テスト目的の振る舞いをさせることができるからです。 たとえば、「現在時刻を返却しているメソッド」を加工して、テストの目的にあった返却値を、常に返却するようにする。などです。 この MockObjectに差し替え の方法も、いろんなアプローチがあります。
などで紹介されています。 「AspectJおよび疑似オブジェクトによる柔軟なテスト」と、「Virtual Mock Objects using AspectJ with JUNIT」の考え方は、あるプロジェクトで実践したことがあります。 なかでも、Virtual Mock Objectsは、いままでチャレンジしてきたテスト方法のなかで、一番気に入っている方法です。 問題は、「Virtual Mock Objects using AspectJ with JUNIT」のサイトからダウンロードできるサンプルコードをみればわかりますが、 pointcutで、「execution(* *.*(..))」とし、すべてのクラスのすべてのメソッドを加工しているため、ソースのコンパイルとweavingに、とても時間がかかることです。 さらに、ANTで、CoverageReportを出力すると、かなり時間がかかりました。 プロジェクト全体のテストは、夜間バッチなどで行えばいいのですが、開発中は、作成中部分だけをテスト実行し、CoverageReportを見たくなります。 そこで、djUnitは、なんとか工夫して、「快適なVirtual Mock Objects」を目指しています。 Inputを変化させ、Outputを検証する ΛMockObjectを使用した単体テストで、検証漏れとなってしまいがちだと思われることを書きます。 JUnitのテストは、「y=f(x)」のような関数のテストは、とても簡単にかくことができます。 このような関数は、xがInputで、yがOutputであることがすぐにわかるからです。 しかし、実際に我々が作成するプログラムは、「y=f(x)」のような、検査し易いメソッドばかりではありません。 たとえば、 public void doSomething() のように一見、InputもOutputも、ないように見えるメソッドだってあります。 やはり、メソッドの内容を良く見なければいけません。そして、InputとOutputを探します。
ほかにもありそうですが、我々は、特に 「他のメソッドを呼び出しているときの引数」をOutputと考え、それを検査することを忘れないようにしています。 なぜかというと、 「他のメソッド」は、MockObjectのメソッドである可能性があり、それは、どんなに不正な引数を渡そうと、正常な結果を返却するからです。 |