Menu

FrontPage

Eclipseプラグイン
Eclipse小技集
リファクタリング講座
テスティングフレームワーク JUnit
MSDEについて



The 20 newest affair
2010-09-092010-09-082010-09-072010-09-062010-09-012010-08-312010-08-272010-08-242010-08-232010-08-172010-08-162010-07-302010-07-212010-07-132010-06-272010-06-182010-06-172010-06-16

はじめに

いまさらですが、我々は、単体テストを自動化して、繰り返し実行することには、とてもメリットがあると考えています。
そして、いままで、たくさんの資料や文献を参考にして、Webアプリケーションのテストの自動化にチャレンジしてきました。

最初は、JUnit、dbUnitを使って、ビジネスロジックやDAO、その他のオブジェクトを一塊のロジックとしてとらえ、たくさんのassert文を書いてテストしていました。

それでも、十分な効果があったのですが、なにしろ検査の対象となるロジックが広範囲であるため、いろいろな問題があることがわかりました。

とくに問題だと思ったのは、

  1. テストが複雑になったり、テストパターンが多くなる
  2. テストデータの準備に時間がかかる
  3. 仕様変更が発生すると、テストも修正しなければならないことが多く、その修正に時間がかかる
  4. 確かにJUnitのバーは緑色になっているが、テスト自体の品質が良いかどうかがわからない

といったことがあげられます。
プロジェクト末期になると、プログラムの修正よりも、テストの修正のほうが多いという場合が、多々あり、そのコストは、結構なものになります。

最近では、これらを解決するため

  • 1~3に関しては、検査対象ロジックの範囲を狭くする
    • ひとつのクラスのテストに集中する
  • 4に関しては、テスト対象のロジックを、全ステップ実行したかどうかを、テスト品質の、ひとつの基準とする
    • JCoverageのカバレッジレポートを毎日生成し、チェックする

ということを実践しています。

djUnitは、カバレッジレポートをもっと頻繁に見れるようにしたり、ひとつのクラスのテストに集中しやすくします。

「ひとつのクラスのテストに集中する」ために有効な仕組み、それが、

    Virtual Mock Objects

です。

ひとつのクラスのテストに集中する  Λ


JUnitによるテストは、対象オブジェクトのあるメソッドを実行し、実行後の状態を検証するというのが基本だと思います。
簡単に「ひとつのクラスのテスト」といっても、ほとんどの場合、クラスは他のクラスに依存していますので、

検査対象オブジェクトの、あるメソッドを実行すると、
そのメソッドは、他のクラスをnewし、newしたオブジェクトのメソッドを実行し、
さらにそのメソッドは、他のクラスをnewし、newしたオブジェクトのメソッドを実行し、
さらにそのメソッドは。。。

といったように、複数のクラスのロジックが実行されてしまいます。
このロジックは最終的に、データベースやファイルにアクセスするかもしれませんし、他のマシンと通信するかもしれません。
ありそうなのは、

    現在時刻を取得し、なんらかの処理を行う

かもしれません。

これでは、他のクラスに依存するばかりか、データベースのデータや、ファイルの内容に依存していることになります。
とすると、テストを実行したときの「データの状態」「ファイルの状態」「時間帯」によって、実行後のオブジェクトの状態が変化してしまいます。
これでは、テストを実行するたびに結果が変化しますので、正しいテストとはいえないと思います。

こういった依存関係があるなかで、検査対象クラスだけに集中するには、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を探します。

  • Input
    • 他のメソッドを呼び出しているときは、その返却値をInputと考える
    • フィールド(メンバ変数)を参照しているときは、そのフィールドをInputと考える

  • Output
    • 他のメソッドを呼び出しているときは、その引数をOutputと考える
    • フィールド(メンバ変数)に代入しているときは、そのフィールドをOutputと考える

ほかにもありそうですが、我々は、特に
「他のメソッドを呼び出しているときの引数」
をOutputと考え、それを検査することを忘れないようにしています。

なぜかというと、
「他のメソッド」は、MockObjectのメソッドである可能性があり、それは、どんなに不正な引数を渡そうと、正常な結果を返却する
からです。


Attached File: [Attached File All List]

Lastmodified: 2007-07-30 (月) 16:19:30 (1136d)