では実際にjMockを使ってみます。
メインクラスを Sample、外部インターフェイスを External とします。
public interface External {
void someExternal();
}
public class Sample {
private External external;
public void some() {
System.out.println("some start");
external.someExternal();
System.out.println("some end");
}
public void setExternal(External external) {
this.external = external;
}
}
今回使用するのはjMock2(JDK1.5以上版)の 2.1.0 です。
JUnit4を使います。
ダウンロードしてきたら、以下のファイルにクラスパスを通します。
jmock-2.1.0.jar jmock-junit4-2.1.0.jar hamcrest-api-1.0.jar hamcrest-library-1.0.jar
やや多いように感じますが、ファイルサイズは合計で280KB程度です。
これに対応するテストケースを書いてみましょう。
とりあえず説明は後にして、サンプルテストコードを載せてしまいます。
@RunWith(JMock.class)
public class SampleTest {
private Mockery context = new JUnit4Mockery();
private External external;
private Sample instance;
@Before
public void setUp() throws Exception {
external = context.mock(External.class);
instance = new Sample();
instance.setExternal(external);
}
@Test
public void testSome() {
context.checking(new Expectations() {{
allowing(external).someExternal();
}});
instance.some();
}
}
正直慣れるまではこのコードを理解するのが難しいかとは思いますが、
これから徐々に説明していきます。
setUpなどはとりあえず置いといて、testSome() メソッドの中身を見てみましょう。context.checking() と instance.some() の2文だけです。
instance は setUp メソッドの中で初期化されています。
このUTを実行すると、テストは成功するはずです。
では仮に、context.checking() 文をコメントアウトしてテストを実行してみます。
java.lang.AssertionError: unexpected invocation: external.someExternal() no expectations specified: did you forget to start an expectation with a cardinality clause?
こんな感じのエラーが発生してしまいました。
何故でしょうか?
instance.some() メソッドに処理が移ると、まず標準出力に
some start
と出力されます。ここまでは大丈夫ですね。では次です。
external.someExternal();
この文を実行しようとして、jMockは先程の例外を発生させたのです。
ここで思い出しましょう。external はモックオブジェクトなのです。
そして何も定義されていません。
つまり、someExternal() を実行しようとしたのですが
実体が定義されていないので「何をしていいかわからない」のです。
通常、インスタンスがあるのにメソッドの実体が定義されていないなんて事は
有り得ないのですが、モックオブジェクトでは仮想的にそういう事が有り得ます。
初めはとっつきにくいこの概念ですが、いずれ慣れるはずです。
では何故、初めに実行したコードでは例外が発生しなかったのでしょうか。
これが、jMock を理解する上で最も重要な「期待値の設定」という話に繋がります。
ではもう一度、コメントアウトしたコードを見てみましょう。
context.checking(new Expectations() {{
allowing(external).someExternal();
}});
1行目と3行目はjMockの習わしだと思って軽く飛ばします。
肝心なのは2行目です。この文が何を意味しているかというと…
external インスタンスが someExternal() メソッドを実行することを期待している
これが先程話した「期待値の設定」です。
つまり、someExternal() を実行しようとしたときに
予め「期待値を設定」しておくことで
「何をしていいかわからない」ではなく
「確かにこのメソッドが実行された」と jMock が判断して
処理が続けられるのです。
戻り値については次回以降で説明します。
最後に、放っておいた setUp メソッドについて簡単に説明します。
external = context.mock(External.class); instance = new Sample(); instance.setExternal(external);
1行目で、モックオブジェクトを生成しています。
内部的には Proxy クラスによってリフレクションを使ったりしていますが
詳細は知らなくても構いません。
2行目で Sample クラスのインスタンスを生成し、
3行目で先程作成したモックオブジェクトをSampleクラスにセットしています。
では続いて、戻り値を持つメソッドについて説明していきましょう。