Mockitos ArgumentCaptoreを使用して特定のタイプのリストをキャプチャする方法はありますか。これは機能しません:
ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(ArrayList.class);
@ Captorアノテーション を使用すると、ネストされたジェネリックの問題を回避できます。
@RunWith(MockitoJUnitRunner.class)
public class Test{
@Mock
private Service service;
@Captor
private ArgumentCaptor<ArrayList<SomeType>> captor;
@Before
public void init(){
MockitoAnnotations.initMocks(this);
}
@Test
public void shouldDoStuffWithListValues() {
//...
verify(service).doStuff(captor.capture()));
}
}
ええ、これは一般的なジェネリックの問題であり、mockito固有のものではありません。
ArrayList<SomeType>
にはクラスオブジェクトがないため、このようなオブジェクトをClass<ArrayList<SomeType>>
を必要とするメソッドにタイプセーフに渡すことはできません。
オブジェクトを正しい型にキャストできます。
Class<ArrayList<SomeType>> listClass =
(Class<ArrayList<SomeType>>)(Class)ArrayList.class;
ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(listClass);
これにより、安全でないキャストに関する警告が表示されます。もちろん、ArgumentCaptorは、おそらく要素を検査せずにArrayList<SomeType>
とArrayList<AnotherType>
を実際に区別できません。
(他の回答で述べたように、これは一般的なジェネリックの問題ですが、@Captor
注釈を使用した型安全性の問題に対するMockito固有のソリューションがあります。ArrayList<SomeType>
とArrayList<OtherType>
を区別できません。)
tenshi sのコメントもご覧ください。元のコードを PaŭloEbermann からこれに変更できます(非常に簡単です)。
final ArgumentCaptor<List<SomeType>> listCaptor
= ArgumentCaptor.forClass((Class) List.class);
古いJavaスタイル(型セーフでないジェネリック)のセマンティクスを恐れていない場合、これも機能し、かなり単純です ':
ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
verify(subject.method(argument.capture()); // run your code
List<SomeType> list = argument.getValue(); // first captured List, etc.
List<String> mockedList = mock(List.class);
List<String> l = new ArrayList();
l.add("someElement");
mockedList.addAll(l);
ArgumentCaptor<List> argumentCaptor = ArgumentCaptor.forClass(List.class);
verify(mockedList).addAll(argumentCaptor.capture());
List<String> capturedArgument = argumentCaptor.<List<String>>getValue();
assertThat(capturedArgument, hasItem("someElement"));
@tenshiと@pkalinowのコメント(@rogerdpackへの称賛)に基づいて、以下は "チェックされていないまたは安全でない操作を使用する"を無効にするリスト引数キャプターを作成するための簡単なソリューションです警告:
@SuppressWarnings("unchecked")
final ArgumentCaptor<List<SomeType>> someTypeListArgumentCaptor =
ArgumentCaptor.forClass(List.class);
完全な例 here および対応するCIビルドとテストの実行 here 。
私たちのチームはしばらくの間、単体テストでこれを使用しており、これは私たちにとって最も簡単なソリューションのように見えます。
Androidアプリでのアクティビティのテストでも同じ問題が発生しました。 ActivityInstrumentationTestCase2
を使用しましたが、MockitoAnnotations.initMocks(this);
は機能しませんでした。それぞれのフィールドを持つ別のクラスでこの問題を解決しました。例えば:
class CaptorHolder {
@Captor
ArgumentCaptor<Callback<AuthResponse>> captor;
public CaptorHolder() {
MockitoAnnotations.initMocks(this);
}
}
次に、アクティビティテストメソッドで:
HubstaffService hubstaffService = mock(HubstaffService.class);
fragment.setHubstaffService(hubstaffService);
CaptorHolder captorHolder = new CaptorHolder();
ArgumentCaptor<Callback<AuthResponse>> captor = captorHolder.captor;
onView(withId(R.id.signInBtn))
.perform(click());
verify(hubstaffService).authorize(anyString(), anyString(), captor.capture());
Callback<AuthResponse> callback = captor.getValue();
以前のバージョンのjunitでは、次のことができます
Class<Map<String, String>> mapClass = (Class) Map.class;
ArgumentCaptor<Map<String, String>> mapCaptor = ArgumentCaptor.forClass(mapClass);