web-dev-qa-db-ja.com

mockitoを使用して特定のタイプのリストをキャプチャする方法

Mockitos ArgumentCaptoreを使用して特定のタイプのリストをキャプチャする方法はありますか。これは機能しません:

ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(ArrayList.class);
254

@ 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()));
    }
}
455
crunchdog

ええ、これは一般的なジェネリックの問題であり、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);
123
Paŭlo Ebermann

古い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.
13
rogerdpack
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"));
7
kkmike999

@tenshiと@pkalinowのコメント(@rogerdpackへの称賛)に基づいて、以下は "チェックされていないまたは安全でない操作を使用する"を無効にするリスト引数キャプターを作成するための簡単なソリューションです警告:

@SuppressWarnings("unchecked")
final ArgumentCaptor<List<SomeType>> someTypeListArgumentCaptor =
    ArgumentCaptor.forClass(List.class);

完全な例 here および対応するCIビルドとテストの実行 here

私たちのチームはしばらくの間、単体テストでこれを使用しており、これは私たちにとって最も簡単なソリューションのように見えます。

2
mrts

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();
1

以前のバージョンのjunitでは、次のことができます

Class<Map<String, String>> mapClass = (Class) Map.class;
ArgumentCaptor<Map<String, String>> mapCaptor = ArgumentCaptor.forClass(mapClass);
1
quzhi65222714