Cómo capturar una lista de tipo específico con mockito

5 minutos de lectura

avatar de usuario
Andreas Koberle

¿Hay alguna manera de capturar una lista de un tipo específico usando mockitos ArgumentCaptore? Esto no funciona:

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

  • Encuentro que es una idea terrible usar una implementación de lista concreta aquí (ArrayList). Siempre puedes usar List interfaz, y si quiere representar el hecho de que es covariante, entonces puede usar extends: ArgumentCaptor<? extends List<SomeType>>

    – tenshi

    4 de septiembre de 2013 a las 10:19


avatar de usuario
perro crujiente

El problema de los genéricos anidados se puede evitar con el anotación @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()));
    }
}

  • prefiero usar MockitoAnnotations.initMocks(this) en el @Before método en lugar de usar un corredor que excluye la capacidad de usar otro corredor. Sin embargo, +1, gracias por señalar la anotación.

    – Juan B.

    12 de octubre de 2012 a las 11:14

  • No estoy seguro de que este ejemplo esté completo. Obtengo… Error: (240, 40) Java: es posible que el captor de variables no se haya inicializado. Me gusta la respuesta de Tenshi a continuación.

    – Michael Dausman

    17 de octubre de 2014 a las 6:13

  • Me encontré con el mismo problema y encontré esta publicación de blog que me ayudó un poco: blog.jdriven.com/2012/10/…. Incluye un paso para usar MockitoAnnotations.initMocks después de colocar la anotación en su clase. Una cosa que noté es que no puedes tenerlo dentro de una variable local.

    – Pendiente de roble

    19 de noviembre de 2015 a las 14:46

  • Normalmente prefiero List en lugar de ArrayList en la declaración de Captor: ArgumentCaptor> captor;

    – Alan Teales

    11 de julio de 2018 a las 10:56

  • initMocks(this) debe ser reemplazado con openMocks(this)ya que el primero está en desuso

    – Florian Koch

    30 de junio de 2021 a las 15:03

avatar de usuario
Pablo Ebermann

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

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

Puede convertir el objeto al tipo correcto:

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

Esto le dará algunas advertencias sobre lanzamientos inseguros y, por supuesto, su ArgumentCaptor realmente no puede diferenciar entre ArrayList<SomeType> y ArrayList<AnotherType> sin tal vez 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 el @Captor anotación. Todavía no puede distinguir entre un ArrayList<SomeType> y un ArrayList<OtherType>.)

Editar:

Eche también un vistazo al comentario de tenshi. Puede cambiar el código original a esta versión simplificada:

final ArgumentCaptor<List<SomeType>> listCaptor
        = ArgumentCaptor.forClass((Class) List.class);

  • El ejemplo que mostraste se puede simplificar, basado en el hecho de que java hace inferencia de tipos para las llamadas a métodos estáticos: ArgumentCaptor<List<SimeType>> argument = ArgumentCaptor.forClass((Class) List.class);

    – tenshi

    4 de septiembre de 2013 a las 10:09

  • Para deshabilitar el utiliza operaciones no controladas o inseguras advertencia, use el @SuppressWarnings("unchecked") anotación encima de la línea de definición del capturador de argumentos. Además, echando a Class es redundante

    – señora

    22 de febrero de 2018 a las 18:32

  • el casting a Class no es redundante en mis pruebas.

    – Wim Deblauwe

    26 de abril de 2018 a las 19:15

avatar de usuario
rogerdpack

Si no tiene miedo de la semántica antigua de estilo Java (genérica no segura), esto también funciona y es 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.

  • Puede agregar @SuppressWarnings(“rawtypes”) antes de la declaración para deshabilitar las advertencias.

    – pkalinow

    13 de agosto de 2015 a las 9:28

avatar de usuario
kkmike999

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

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 argumentos de lista que también deshabilita el “utiliza operaciones no controladas o inseguras” advertencia:

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

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

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

avatar de usuario
quzhi65222714

Para una versión anterior de junit, puede hacer

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

avatar de usuario
Timofey Orishenko

Tuve el mismo problema con la actividad de prueba en mi aplicación de Android. solía ActivityInstrumentationTestCase2 y MockitoAnnotations.initMocks(this); no funcionó Resolví este problema con otra clase con 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();

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad