心地良すぎるモックライブラリ Mockito 〜その3〜


心地良すぎるモックライブラリ Mockito 〜その2〜 - A Memorandumの続き

モックメソッドからの戻り値のデフォルト設定(Since 1.7)

モックメソッドからのデフォルトの戻り値を、モック定義時に Answer として指定することができます。組込で定義されている Answer を使用ることも

Foo mock = mock(Foo.class, Mockito.RETURNS_SMART_NULLS);

カスタマイズしたものをデフォルトで返却するようにできます。

Foo mockTwo = mock(Foo.class, new YourOwnAnswer()); 


組込では以下のものが定義されています。

  • Mockito.CALLS_REAL_METHODS 実際のメソッドを呼び出そうとする
  • Mockito.RETURNS_DEEP_STUBS 再帰的にスタブを返却する
  • Mockito.RETURNS_DEFAULTS グローバル定義に従い、該当しない場合空のモックを返却しようとする
  • Mockito.RETURNS_MOCKS プリミティブラッパの場合ゼロ、コレクションの場合空のコレクション等、空のモックを返却しようとする
  • Mockito.RETURNS_SMART_NULLS モックメソッドの戻り値がnullだった場合に分かりやすいスタックトレースを出力


本機能を適切なテストで使用するのは特殊なケースと心得てください。レガシーなシステムのテストを容易にしますが、通常はテストが容易なコードとすることで使用する場面は少なくなるはずです。

引数をキャプチャしてアサーションで利用する(Since 1.8.0)

Mockito の verify() では equals() による同値性の検証が行われます。通常この方法は簡素であり推奨されますが、引数のキャプチャにて以下のようなアサーションを行うことができます。

ArgumentCaptor<Person> argument = ArgumentCaptor.forClass(Person.class);
verify(mock).doSomething(argument.capture());
assertEquals("John", argument.getValue().getName());


これにより対象を選択的にアサートすることができます。
ArgumentCaptor は verify 時に使用するようにしてください。

部分的に実メソッドを呼び出すモック(Since 1.8.0)

spy() を使用する場合は以下のようにしました。

List list = spy(new LinkedList());


spy() より混乱のない形で部分的に実メソッドを呼び出すモックを定義できます。次のようにします。

Foo mock = mock(Foo.class);
when(mock.someMethod()).thenCallRealMethod();


サードパーティーライブラリやレガシーコードに対してモックを利用する場合は上記が役に立つでしょう。しかし、きれいにデザインされたクラス設計では、部分的に実メソッドを呼び出すモックを使用することはほとんどないはずです。

モックのリセット(Since 1.8.0)

モックのリセットが可能です。通常小さなテストしやすい美しいコードではリセットを使用することはないはずです。リセットを使用する場合は設計を見直す必要があるサインとなります。とはいえ、リセットが可能です。

List mock = mock(List.class);
when(mock.size()).thenReturn(10);
mock.add(1);
   
reset(mock);

振舞駆動開発のためのエイリアス(Since 1.8.0)

振舞駆動開発(Behavior Driven Development)型で、//given //when //then のコメントでテストを書くことはとても良いことです。これらのコメントと Mockito のAPIとは名称が異なるため、BDDMockito にて別名の API が提供されています。

import static org.mockito.BDDMockito.*;
 
Seller seller = mock(Seller.class);
Shop shop = new Shop(seller);
 
public void shouldBuyBread() throws Exception {
   //given  
   given(seller.askForBread()).willReturn(new Bread());
   
   //when
   Goods goods = shop.buyBread();
   
   //then
   assertThat(goods, containBread());
}  

serializableモック(Since 1.8.1)

モックを serializable として作成可能です。単体テストのレベルでは通常必要とはなりませんが。以下のようになります。

List serializableMock = mock(List.class, withSettings().serializable());


spy オブジェクトに serializable が必要な場合は以下のようにします。

List list = new ArrayList();
List spy = mock(ArrayList.class, withSettings()
                .spiedInstance(list)
                .defaultAnswer(CALLS_REAL_METHODS)
                .serializable());

新しい@Captor, @Spy, @InjectMocks アノテーション(Since 1.8.3)

リリース 1.8.3 にて以下のアノテーションが追加されました。

  • @Captor - ArgumentCaptor オブジェクト生成用
  • @Spy - spy オブジェクト生成用
  • @InjectMocks - セッターインジェクションにて該当のモックとスパイを自動的にインジェクト

なお、@Mockと同様に MockitoAnnotations.initMocks(Object) による初期化が必要です。

タイムアウト検証(Since 1.8.5)

タイムアウト検証が可能となっています。以下の2つのコードは100msec中にsomeMethod()が呼び出されたかの妥当性検証を行います。

   verify(mock, timeout(100)).someMethod();
   verify(mock, timeout(100).times(1)).someMethod();


以下のように、指定時間内に2回、最後の2回が指定時間内に、カスタマイズしたVerificationModeを指定、等が可能です。

verify(mock, timeout(100).times(2)).someMethod();
verify(mock, timeout(100).atLeast(2)).someMethod();
verify(mock, new Timeout(100, yourOwnVerificationMode)).someMethod();


なお、現状では InOrder においてタイムアウト検証はできません。





Test-Driven Development with Mockito

Test-Driven Development with Mockito

Mockito Cookbook

Mockito Cookbook