Cómo capturar una lista de tipos específicos con mockito


Hay una manera de capturar una lista de tipo específico usando mockitos ArgumentCaptore. Esto no funciona:

ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(ArrayList.class);
Author: rogerdpack, 2011-04-09

6 answers

El problema de los genéricos anidados 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()));
    }
}
 389
Author: crunchdog,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-05 16:29:40

Sí, este es un problema genérico general, no específico de mockito.

No hay un objeto de clase para ArrayList<SomeType>, y por lo tanto no se puede pasar de forma segura un objeto de este tipo a un método que requiera un Class<ArrayList<SomeType>>.

Puede enviar 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 los moldes inseguros, y por supuesto su ArgumentCaptor realmente no puede diferenciar entre ArrayList<SomeType> y ArrayList<AnotherType> sin tal vez inspeccionar los elementos.

(Como se menciona en la otra respuesta, si bien este es un problema genérico general, hay 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> y ArrayList<OtherType>.)

Editar:

Echa también un vistazo al comentario de tenshi. Puede cambiar el código original de Paŭlo Ebermann a esto (mucho más simple)

final ArgumentCaptor<List<SomeType>> listCaptor
        = ArgumentCaptor.forClass((Class) List.class);
 109
Author: Paŭlo Ebermann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-04-18 14:55:11

Si no tienes miedo de la antigua semántica de estilo java (no tipo seguro genérico), 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.
 11
Author: rogerdpack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-19 02:11:30
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"));
 6
Author: kkmike999,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-11 10:11:00

Basado en los comentarios de @tenshi y @pkalinow (también felicitaciones a @rogerdpack), la siguiente es una solución simple para crear un captor de argumento de lista que también deshabilita la advertencia "usa operaciones no verificadas o inseguras":

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

Ejemplo completo aquí y la correspondiente compilación y ejecución de prueba de CI aprobada aquí.

Nuestro equipo ha estado usando esto durante algún tiempo en nuestras pruebas unitarias y esta parece la solución más sencilla para nosotros.

 1
Author: mrts,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-30 17:17:36

Tuve el mismo problema con la actividad de prueba en mi aplicación Android. Usé 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 el 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();
 0
Author: Timofey Orischenko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-24 09:48:14