mockitos ArgumentCaptoreを使用して特定のタイプのリストをキャプチャする方法はありますか?これは機能しません:
ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(ArrayList.class);
mockitos ArgumentCaptoreを使用して特定のタイプのリストをキャプチャする方法はありますか?これは機能しません:
ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(ArrayList.class);
回答:
ネストされたジェネリック問題は@Captorアノテーションで回避できます:
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()));
}
}
MockitoAnnotations.initMocks(this)
、@Before
メソッドで使用することを好みます。ただし、+ 1、注釈を指摘していただきありがとうございます。
ええ、これは一般的なジェネリック問題であり、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>
。
(これは一般的なジェネリックの問題であるように、他の回答で述べた、と型の安全性の問題のためにMockito固有のソリューションが存在する@Captor
アノテーションは、それはまだ区別できないArrayList<SomeType>
とArrayList<OtherType>
)。
tenshiのコメントもご覧ください。元のコードをPaŭloEbermannからこれに変更できます(はるかに簡単)
final ArgumentCaptor<List<SomeType>> listCaptor
= ArgumentCaptor.forClass((Class) List.class);
ArgumentCaptor<List<SimeType>> argument = ArgumentCaptor.forClass((Class) List.class);
@SuppressWarnings("unchecked")
、引数キャプター定義行の上にある注釈を使用します。また、へのキャストClass
は冗長です。
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);
ここでの完全な例と、対応するCIのビルドとテストの実行をここで実行します。
私たちのチームはこれまでユニットテストでこれを使用しており、これは私たちにとって最も簡単なソリューションのように見えます。
junitの以前のバージョンでは、次のことができます
Class<Map<String, String>> mapClass = (Class) Map.class;
ArgumentCaptor<Map<String, String>> mapCaptor = ArgumentCaptor.forClass(mapClass);
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();
ありMockitoのGitHubの中に未解決の問題、まさにこの問題については。
テストでアノテーションの使用を強制しない簡単な回避策を見つけました。
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.MockitoAnnotations;
public final class MockitoCaptorExtensions {
public static <T> ArgumentCaptor<T> captorFor(final CaptorTypeReference<T> argumentTypeReference) {
return new CaptorContainer<T>().captor;
}
public static <T> ArgumentCaptor<T> captorFor(final Class<T> argumentClass) {
return ArgumentCaptor.forClass(argumentClass);
}
public interface CaptorTypeReference<T> {
static <T> CaptorTypeReference<T> genericType() {
return new CaptorTypeReference<T>() {
};
}
default T nullOfGenericType() {
return null;
}
}
private static final class CaptorContainer<T> {
@Captor
private ArgumentCaptor<T> captor;
private CaptorContainer() {
MockitoAnnotations.initMocks(this);
}
}
}
ここで何が起こるかというと、@Captor
アノテーションを使用して新しいクラスを作成し、そこにキャプターを挿入します。次に、キャプターを抽出して静的メソッドから返すだけです。
テストでは、次のように使用できます。
ArgumentCaptor<Supplier<Set<List<Object>>>> fancyCaptor = captorFor(genericType());
または、ジャクソンの構文に似ていますTypeReference
:
ArgumentCaptor<Supplier<Set<List<Object>>>> fancyCaptor = captorFor(
new CaptorTypeReference<Supplier<Set<List<Object>>>>() {
}
);
Mockitoは実際にはタイプ情報を必要としないため(たとえば、シリアライザとは異なり)、これは機能します。
ArrayList
)。あなたは、常に使用することができList
ますが、事実を表現したい場合は、それの共変は、あなたが使用できる、インターフェイスを、そしてextends
:ArgumentCaptor<? extends List<SomeType>>