Apakah ada cara untuk menangkap daftar tipe tertentu menggunakan mockitos ArgumentCaptore. Ini tidak berfungsi:
ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(ArrayList.class);
Apakah ada cara untuk menangkap daftar tipe tertentu menggunakan mockitos ArgumentCaptore. Ini tidak berfungsi:
ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(ArrayList.class);
Jawaban:
Masalah generik bersarang dapat dihindari dengan penjelasan @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)
dalam @Before
metode daripada menggunakan pelari yang tidak termasuk kemampuan untuk menggunakan pelari lain. Namun, +1, terima kasih telah menunjukkan anotasi.
Ya, ini masalah umum generik, bukan mockito-spesifik.
Tidak ada objek kelas untuk ArrayList<SomeType>
, dan karenanya Anda tidak bisa mengetik-aman meneruskan objek seperti itu ke metode yang membutuhkan a Class<ArrayList<SomeType>>
.
Anda bisa melemparkan objek ke tipe yang tepat:
Class<ArrayList<SomeType>> listClass =
(Class<ArrayList<SomeType>>)(Class)ArrayList.class;
ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(listClass);
Ini akan memberikan beberapa peringatan tentang gips yang tidak aman, dan tentu saja ArgumentCaptor Anda tidak dapat benar-benar membedakan antara ArrayList<SomeType>
dan ArrayList<AnotherType>
tanpa mungkin memeriksa elemen.
(Seperti yang disebutkan dalam jawaban lain, sementara ini adalah masalah umum umum, ada solusi spesifik Mockito untuk masalah keamanan jenis dengan @Captor
anotasi. Itu masih tidak dapat membedakan antara ArrayList<SomeType>
dan ArrayList<OtherType>
.)
Lihatlah juga komentar tenshi . Anda dapat mengubah kode asli dari Paŭlo Ebermann ke ini (lebih sederhana)
final ArgumentCaptor<List<SomeType>> listCaptor
= ArgumentCaptor.forClass((Class) List.class);
ArgumentCaptor<List<SimeType>> argument = ArgumentCaptor.forClass((Class) List.class);
@SuppressWarnings("unchecked")
anotasi di atas garis definisi penculik argumen. Juga, casting to Class
redundant.
Class
tidak berlebihan dalam pengujian saya.
Jika Anda tidak takut dengan semantik gaya java lama (non type safe generic), ini juga berfungsi dan cukup sederhana:
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"));
Berdasarkan komentar @ tenshi dan @ pkalinow (juga pujian untuk @rogerdpack), berikut ini adalah solusi sederhana untuk membuat daftar argumen penculiknya yang juga menonaktifkan peringatan "penggunaan operasi yang tidak dicentang atau tidak aman" :
@SuppressWarnings("unchecked")
final ArgumentCaptor<List<SomeType>> someTypeListArgumentCaptor =
ArgumentCaptor.forClass(List.class);
Contoh lengkap di sini dan pembangunan CI lulus yang sesuai dan uji coba di sini .
Tim kami telah menggunakan ini selama beberapa waktu dalam unit test kami dan ini sepertinya solusi paling mudah bagi kami.
Untuk versi junit sebelumnya, Anda bisa melakukannya
Class<Map<String, String>> mapClass = (Class) Map.class;
ArgumentCaptor<Map<String, String>> mapCaptor = ArgumentCaptor.forClass(mapClass);
Saya memiliki masalah yang sama dengan aktivitas pengujian di aplikasi Android saya. Saya menggunakan ActivityInstrumentationTestCase2
dan MockitoAnnotations.initMocks(this);
tidak bekerja. Saya memecahkan masalah ini dengan kelas lain dengan bidang masing-masing. Sebagai contoh:
class CaptorHolder {
@Captor
ArgumentCaptor<Callback<AuthResponse>> captor;
public CaptorHolder() {
MockitoAnnotations.initMocks(this);
}
}
Kemudian, dalam metode uji aktivitas:
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();
Ada masalah terbuka di GitHub Mockito tentang masalah ini.
Saya telah menemukan solusi sederhana yang tidak memaksa Anda untuk menggunakan anotasi dalam tes Anda:
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);
}
}
}
Apa yang terjadi di sini adalah bahwa kita membuat kelas baru dengan para @Captor
penjelasan dan menyuntikkan penculiknya ke dalamnya. Kemudian kita cukup mengekstrak penculiknya dan mengembalikannya dari metode statis kami.
Dalam tes Anda, Anda dapat menggunakannya seperti ini:
ArgumentCaptor<Supplier<Set<List<Object>>>> fancyCaptor = captorFor(genericType());
Atau dengan sintaks yang menyerupai Jackson TypeReference
:
ArgumentCaptor<Supplier<Set<List<Object>>>> fancyCaptor = captorFor(
new CaptorTypeReference<Supplier<Set<List<Object>>>>() {
}
);
Ini berfungsi, karena Mockito sebenarnya tidak memerlukan informasi jenis apa pun (tidak seperti serializers, misalnya).
ArrayList
). Anda selalu dapat menggunakanList
antarmuka, dan jika Anda ingin merepresentasikan fakta, bahwa itu kovarian, maka Anda dapat menggunakanextends
:ArgumentCaptor<? extends List<SomeType>>