テストコードでは必須と言ってもいいくらいにお世話になっているモックライブラリ「Mockito」
最低限の使い方というか、実際よく使っているパターンを紹介します。
モックって?
モックライブラリを使ったことがない方は、モックするという事自体に馴染みがないと思います。
例えば、テスト対象のクラスAが別のクラスBに依存している場合に、クラスAのテストコードなのにクラスBを初期化する処理を長々と書いたりするのは余計な手間です。もしクラスBがビジネスロジックなら目も当てられないテストコードが出来上がります。
モックライブラリでは、クラスBをクラスB自身の実装に依存しないmock(ハリボテ)として生成し、更にそのメソッドの戻り値を任意に設定するという事ができます。これにより「クラスBがこういう状態でこういう値を戻す場合のクラスAのテスト」が容易に書けます。
そしてそのモックライブラリの注目株が Mockito です。
mock
Mockito#mockメソッドを使うと、そのクラスの実装に依存しないモックオブジェクトを生成します。
各メソッドの戻り値はモックオブジェクトになりますが、指定したメソッドの戻り値だけを任意に設定できます。
任意の値を戻すようにモックする
Hogeクラスをモックし、Hoge#getFooメソッドの戻り値を任意のFooにします。
Hogeが抽象クラスやインターフェースの場合でも同じようにモックはできます。
Hoge hoge = mock(Hoge.class);
Foo foo = new Foo();
when(hoge.getFoo()).thenReturn(foo);
assertThat(hoge.getFoo(), is(foo));
引数に応じて戻り値を分ける
引数を受け取るメソッドなら、引数に応じて戻り値を変えるようにモックできます。
引数には具体的な値を指定するか Mockito#any〜 メソッドを使いましょう。
各プリミティブ型・ListやSetなどCollection系・可変長引数・任意のクラス、それぞれに対応したany〜メソッドがあるので使い分けてください。
Hoge hoge = mock(Hoge.class);
when(hoge.getFuga(anyInt())).thenReturn(-1); // どんな数が来ても -1 を返す
when(hoge.getFuga(1)).thenReturn(100); // ただし、1の時は100を返す
when(hoge.getFuga(2)).thenReturn(null); // ただし、2の時はnullを返す
assertThat(hoge.getFuga(1), is(100));
assertThat(hoge.getFuga(2), is(nullValue()));
assertThat(hoge.getFuga(999), is(-1));
ネストしたオブジェクトをモックしたい
mockメソッドの第2引数では各種オプションを設定できます。
Mockito#RETURNS_DEEP_STUBSを使うと、Hoge#getFooの戻り値のFooの各メソッドもモックオブジェクトを返すようになります。
これを用いると、Hoge#getFooとFoo#getBarの実装を気にせずに、Foo#getBarの戻り値を任意に設定できます。
Hoge hoge = mock(Hoge.class, RETURNS_DEEP_STUBS);
Bar bar = new Bar();
when(hoge.getFoo().getBar()).thenReturn(bar);
assertThat(hoge.getFoo().getBar(), is(bar));
※再帰的な構造のオブジェクトをRETURNS_DEEP_STUBSでmockして再帰メソッドに食わせると、無限ループになります。思いの外やってしまうミスなので注意して下さい。
spy
Mockito#spyメソッドを使うと、任意のインスタンスの一部だけモックできます。
これは、完全なハリボテを作るmockとは異なり、対象のクラスの実装に依存しつつ、指定したメソッドだけをモックします。
doSomethingの中で呼ばれるsomeMethodだけをモックしたい
spyで得たオブジェクトは、モックしたメソッドの他は元々の実装で動作します。
mockでモックオブジェクトを生成した場合と、メソッドのモックの仕方が異なるので注意が必要です。
例えば、Hoge#someMethodに依存するHoge#doSomethingをテストしたい時、以下のようにHoge#someMethodの戻り値を任意に設定します。
Hoge hoge = spy(new Hoge());
Piyo piyo = new Piyo();
doReturn(piyo).when(hoge).someMethod(any(Bar.class));
Result result = hoge.doSomething();
verify(hoge).someMethod(any(Bar.class)); // someMethodが呼ばれた事をassert
privateなフィールドを任意に設定
dependerが依存しているdependeeフィールドをモックする
Mockitoにはprivateなフィールドをリフレクションで任意に設定するユーティリティが付属しています。
Whitebox.setInternalState(depender, "dependee", dependee);
その他
Mockitoはモックする他にもテストに有用な機能を持っています。
上でもちょっとだけ出てきましたが、メソッドがどのように呼び出されたかをテストするverifyなどです。
verifyでは、対象のメソッドが、どういう順序で、どういう引数で、何回呼び出されたか、といったことをテストできます。