java - unitarios - Cómo capturar una lista de tipos específicos con mockito.
test unitarios junit mockito (6)
¿Hay una manera de capturar una lista de tipo específico utilizando mockitos ArgumentCaptore. Esto no funciona:
ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(ArrayList.class);
Basado en los comentarios de @ tenshi y @ pkalinow (también felicitaciones a @rogerdpack), la siguiente es una solución simple para crear un captador de argumento de lista que también deshabilita la advertencia "utiliza operaciones no comprobadas o no seguras" :
@SuppressWarnings("unchecked")
final ArgumentCaptor<List<SomeType>> someTypeListArgumentCaptor =
ArgumentCaptor.forClass(List.class);
Ejemplo completo here y ejecución correspondiente de compilación y prueba de CI here .
Nuestro equipo ha estado usando esto durante algún tiempo en nuestras pruebas unitarias y esta parece ser la solución más sencilla para nosotros.
El problema genérico anidado se puede evitar con la anotación @Captor :
@RunWith(MockitoJUnitRunner.class)
public class Test{
@Mock
private Service service;
@Captor
private ArgumentCaptor<ArrayList<SomeType>> captor;
@Test
public void shouldDoStuffWithListValues() {
//...
verify(service).doStuff(captor.capture()));
}
}
Sí, este es un problema genérico general, no específico de mockito.
No hay ningún objeto de clase para ArrayList<SomeType>
y, por lo tanto, no puede escribir con seguridad dicho objeto a un método que requiera una Class<ArrayList<SomeType>>
.
Puedes lanzar el objeto al tipo correcto:
Class<ArrayList<SomeType>> listClass =
(Class<ArrayList<SomeType>>)(Class)ArrayList.class;
ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(listClass);
Esto dará algunas advertencias sobre conversiones no seguras y, por supuesto, su ArgumentCaptor realmente no puede diferenciar entre ArrayList<SomeType>
y ArrayList<AnotherType>
sin inspeccionar los elementos.
(Como se mencionó en la otra respuesta, si bien este es un problema genérico general, existe una solución específica de Mockito para el problema de seguridad de tipo con la anotación @Captor
. Todavía no puede distinguir entre un ArrayList<SomeType>
ArrayList<OtherType>
y un ArrayList<OtherType>
.)
Editar:
Echa un vistazo también al comentario de tenshi . Puede cambiar el código original de Paŭlo Ebermann a este (mucho más simple)
final ArgumentCaptor<List<SomeType>> listCaptor
= ArgumentCaptor.forClass((Class) List.class);
Si no le teme a la antigua semántica de estilo java (genérico no de tipo seguro), esto también funciona y es razonablemente simple:
ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
verify(subject.method(argument.capture()); // run your code
List<SomeType> list = argument.getValue(); // first captured List, etc.
Tuve el mismo problema con la actividad de prueba en mi aplicación de Android. Utilicé ActivityInstrumentationTestCase2
y MockitoAnnotations.initMocks(this);
no funcionó Resolví este problema con otra clase con el campo respectivamente. Por ejemplo:
class CaptorHolder {
@Captor
ArgumentCaptor<Callback<AuthResponse>> captor;
public CaptorHolder() {
MockitoAnnotations.initMocks(this);
}
}
Luego, en método de prueba de actividad:
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();
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"));