読者です 読者をやめる 読者になる 読者になる

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

Java

API的に EasyMock と大きな違いはありませんが、使用感としては格段に心地良い Mockito。

本家 http://mockito.org/ のドキュメント(というかJavaDoc)をベースにメモ。

EasyMockとの違い

  • Mockito では record モードと replay モードを切り替える必要がない
  • Mockito で作成するモックは常に、EasyMock で言う NiceMock となる
  • スタブメソッドの妥当性検証が常にオプション扱い

大きくは以上となります。具体的に、EasyMock では

import static org.easymock.classextension.EasyMock.*;         

List mock = createNiceMock(List.class);                                       
expect(mock.get(0)).andStubReturn("one");                             
mock.clear();
  
replay(mock);
  
someCodeThatInteractsWithMock();                                                      
  
verify(mock);


Mockito では

import static org.mockito.Mockito.*;
  
List mock = mock(List.class);
when(mock.get(0)).thenReturn("one");

someCodeThatInteractsWithMock();
  
verify(mock).clear();

となります。replay() によるモードの切替が不要であり、verify() は Mockito においては必要なスタブメソッドの呼び出しのみを選択的に検証するようになっています。

Eclipse での利用に際して

static import の設定をしておくのが吉。設定画面で、Java - Editor - Content Assist - Favorits にて以下の設定。

  • New Type
    • org.mockito.Matchers
    • org.mockito.Mockito

詳細はちらでJUnit Hamcrest の eclipse 設定 - A Memorandum

org.mockito.Mockito

Mockito の利用方法は org.mockito.Mockito の JavaDoc に体系的にまとめられています。ここでは基本的な内容をかいつまんで。

モックの利用と妥当性検証

コードをシンプルに保つために、Mockito をスタティックインポートしておきます。

import static org.mockito.Mockito.*;


ここでは List インターフェースに対してモックを作成し、モックのメソッドを呼び出してみます。

List mockedList = mock(List.class);
mockedList.add("one");
mockedList.clear();


モックに対してメソッド呼び出しの妥当性検証を行うには verify() を使用します。ここでは add() メソッドが引数 "one" として呼び出されたかどうか、clear() メソッドが呼び出されたかどうかを検証しています。

verify(mockedList).add("one");
verify(mockedList).clear();

作成されたモックはモックに対する全ての相互作用(メソッド呼び出し)を記録しています。Mockito では、この相互作用の中から必要のあるものを選択的に検証するようになっています。

ここではインターフェースからモックを作成しましたが、もちろん具象クラスからモックを作成することもできます。

スタブメソッドの定義

モックオブジェクトにスタブメソッドを定義するには when() thenReturn() を使用します。EasyMock の expect() andReturn() に該当するものとなります。

LinkedList mockedList = mock(LinkedList.class);

when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(1)).thenThrow(new RuntimeException());


スタブメソッドを動作を定義したので、モックに対するメソッド呼び出しにて定義した戻り値が返却されます。

mockedList.get(0);    // firstが返却される
mockedList.get(1);    // RuntimeException が投げられる
mockedList.get(999);  // nul が返却される


モックに対してメソッドが呼び出されたかどうかを検証するには以下のようにします。

verify(mockedList).get(0);

通常このような検証は不要です。返却値を定義しており、その返却値を持って動くテストコードを記述するため、メソッドが呼ばれたことは暗黙的に確認されるためです。

  • デフォルトで、モックのメソッドはnull または空のコレクション、適切なプリミティブ/プリミティブラッパー値を返却します(例:int/Integerの場合は 0, boolean/Booleanの場合はfalse, ..)
  • スタブの動作は上書き可能なので、テストコードの共通的な部分はsetupメソッドで定義し、それぞれのテストメソッド内で必要な部分だけを上書き設定できます
  • メソッドに対して戻り値を定義すると、何度呼び出しても同じ戻り値が得られます
  • 同じメソッドの同じ引数に対して定義を上書きした場合は最後に定義したものが有効になります

引数の照合

Mockito での引数の照合は equals() による値の等価性にて行われます。より柔軟に引数の照合を行う matcher も用意されています。
組込の anyInt() によりどんなint値にもマッチさせることができます。

when(mockedList.get(anyInt())).thenReturn("element");
mockedList.get(999);  // "element"が返却される


verify にて使用することもできます。

verify(mockedList).get(anyInt());

その他の組込の matcher はこちらを参照してください。matcher をカスタマイズすることもできます。 ArgumentMatcher を参照してください。


hamcrest を使用する場合は argThat の引数として hamcrest の matcher を指定できます。

when(mockedList.contains(argThat(XXX))).thenReturn("element");


引数のマッチングはオブジェクトを直接使用するのではなく、anyX() を使用することで簡素な記述となり、リファクタリングの影響を小さく抑えることができます。
より厳密な引数のマッチングには ArgumentCaptor が利用できます(後述します)。


引数 matchers の利用時、以下は正しくない点に注意してください。

verify(mock).someMethod(anyInt(), anyString(), "third argument");

matchers を使用する場合は、3つ目の "third argument" を以下のようにする必要があります。

verify(mock).someMethod(anyInt(), anyString(), eq("third argument"));

メソッド呼び出しの妥当性検証

モックの該当メソッドの呼び出し回数の妥当性を検証するには times を使用します。add() が2回呼び出されていることを検証するには以下のようにします。

verify(mockedList, times(2)).add("foo");


verify のデフォルトはメソッドが1回呼び出されていることが検証されるため、以下のコードは同等となります。

verify(mockedList).add("foo");
verify(mockedList, times(1)).add("foo");


反対にメソッド呼び出しが行われないことを検証する場合は never() を指定します。

verify(mockedList, never()).add("foo");


その他、最後の1回、少なくとも2回、多くとも5回といった指定もできます。

verify(mockedList, atLeastOnce()).add("foo");
verify(mockedList, atLeast(2)).add("foo");
verify(mockedList, atMost(5)).add("foo");

voidメソッドから例外を返却

モックのvoidメソッドが呼ばれた場合に例外を返却する場合は以下のように doThrow を使用します。

doThrow(new RuntimeException()).when(mockedList).clear();

mockedList.clear(); // 例外が投げられる


次回に続く。。





Test-Driven Development with Mockito

Test-Driven Development with Mockito

Mockito Cookbook

Mockito Cookbook