mockitoで特定のタイプのリストをキャプチャする方法


301

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

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

8
ここで具体的なリスト実装を使用するのはひどい考えです(ArrayList)。あなたは、常に使用することができListますが、事実を表現したい場合は、それの共変は、あなたが使用できる、インターフェイスを、そしてextendsArgumentCaptor<? extends List<SomeType>>
天使

回答:


533

ネストされたジェネリック問題は@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()));
    }
}

70
別のランナーを使用する能力を排除するランナーを使用するよりもMockitoAnnotations.initMocks(this)@Beforeメソッドで使用することを好みます。ただし、+ 1、注釈を指摘していただきありがとうございます。
John B

4
この例が完全かどうかはわかりません。取得します...エラー:(
240、40

1
私は同じ問題に出くわしました、そして私を少し助ける次のブログ投稿を見つけました:blog.jdriven.com/2012/10/…。クラスに注釈を付けた後にMockitoAnnotations.initMocksを使用する手順が含まれています。私が気づいたことの1つは、ローカル変数内では使用できないことです。
SlopeOak 2015年

1
@ chamzz.dot ArgumentCaptor <ArrayList <SomeType >> captor; 「SomeType」の配列をすでにキャプチャしています<-特定のタイプですよね?
Miguel R. Santaella

1
私は通常、Captor宣言でArrayListではなくListを優先します。ArgumentCaptor <List <SomeType >> captor;
Miguel R. Santaella

146

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

49
:あなたが示した例では、Javaは、静的メソッド呼び出しの型推論を行っているという事実に基づいて、簡素化することができますArgumentCaptor<List<SimeType>> argument = ArgumentCaptor.forClass((Class) List.class);
天使

4
未チェックまたは安全でない操作の使用警告を無効にするに@SuppressWarnings("unchecked")、引数キャプター定義行の上にある注釈を使用します。また、へのキャストClassは冗長です。
mrts 2018

1
へのキャストClassは、私のテストでは冗長ではありません。
Wim Deblauwe

16

古い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.

2
宣言の前に@SuppressWarnings( "rawtypes")を追加して、警告を無効にすることができます。
pkalinow 2015

9
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"));

4

@tenshiのコメントと@pkalinowのコメント(@rogerdpackへの称賛も含む)に基づく、以下は、「チェックされていない、または安全でない操作の使用」警告も無効にするリスト引数キャプターを作成するための簡単な解決策です。

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

ここでの完全な例と、対応するCIのビルドとテストの実行をここで実行します

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


2

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

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

1

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

0

あり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は実際にはタイプ情報を必要としないため(たとえば、シリアライザとは異なり)、これは機能します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.