¿Puede Mockito capturar argumentos de un método llamado varias veces?

5 minutos de lectura

Tengo un método al que se llama dos veces y quiero capturar el argumento de la segunda llamada al método.

Esto es lo que he intentado:

ArgumentCaptor<Foo> firstFooCaptor = ArgumentCaptor.forClass(Foo.class);
ArgumentCaptor<Foo> secondFooCaptor = ArgumentCaptor.forClass(Foo.class);
verify(mockBar).doSomething(firstFooCaptor.capture());
verify(mockBar).doSomething(secondFooCaptor.capture());
// then do some assertions on secondFooCaptor.getValue()

pero obtengo un TooManyActualInvocations Excepción, ya que Mockito piensa que doSomething sólo debe llamarse una vez.

¿Cómo puedo verificar el argumento de la segunda llamada de doSomething?

avatar de usuario
proactivo

Creo que debería ser

verify(mockBar, times(2)).doSomething(...)

Muestra de maqueta javadoc:

ArgumentCaptor<Person> peopleCaptor = ArgumentCaptor.forClass(Person.class);
verify(mock, times(2)).doSomething(peopleCaptor.capture());

List<Person> capturedPeople = peopleCaptor.getAllValues();
assertEquals("John", capturedPeople.get(0).getName());
assertEquals("Jane", capturedPeople.get(1).getName());

  • ¿Puedes capturar los argumentos pasados ​​a doSomething() en cada invocación separada con esto?

    – mate b

    12 de mayo de 2011 a las 17:19

  • Cabe señalar que en caso de que hagas algo como esto: Person person = new Person("John"); doSomething(person); person.setName("Jane"); doSomething(person); el argumento capturado será el mismo dos veces (porque en realidad es el mismo objeto de persona), por lo que capturedPeople.get(0).getName() == capturedPeople.get(1).getName() == "Jane" ver también groups.google.com/forum/#!msg/mockito/KBRocVedYT0/5HtARMl9r2wJ.

    – Asmaier

    4 de diciembre de 2014 a las 14:46


  • Esto está bien, pero ¿cómo puedo probar dos invocaciones de objetos de tipo diferente? Por ejemplo ExecutorService.submit(nuevo MyRunableImpl()); y luego ExecutorService.submit(new MyAnotherRunableImpl()) ?

    – León

    14 de diciembre de 2015 a las 14:16

  • Si uno necesita manejar el caso descrito por @asmaier, publiqué una respuesta aquí: stackoverflow.com/a/36574817/1466267

    – Camionero espacial

    5 de agosto de 2016 a las 11:43

  • Para cualquiera que todavía se pregunte la respuesta a la pregunta de Leon, usaría la clase base común (Runnable) y, si es necesario, realice una verificación de tipo más específica en el argumento capturado.

    – Mateo Lee

    24/10/2018 a las 16:25

avatar de usuario
Maciej Dobrowolski

Desde Mockito 2.0 también existe la posibilidad de usar el método estático Matchers.argEso(ArgumentMatcher). Con la ayuda de Java 8, ahora es mucho más limpio y legible escribir:

verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("OneSurname")));
verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("AnotherSurname")));

Si está atado a una versión inferior de Java, tampoco hay nada malo:

verify(mockBar).doSth(argThat(new ArgumentMatcher<Employee>() {
        @Override
        public boolean matches(Object emp) {
            return ((Employee) emp).getSurname().equals("SomeSurname");
        }
    }));

Por supuesto, ninguno de ellos puede verificar el orden de las llamadas, para lo cual debe usar En orden :

InOrder inOrder = inOrder(mockBar);

inOrder.verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("FirstSurname")));
inOrder.verify(mockBar).doSth(argThat((arg) -> arg.getSurname().equals("SecondSurname")));

Por favor, eche un vistazo a mockito-java8 proyecto que hace posible realizar llamadas como:

verify(mockBar).doSth(assertArg(arg -> assertThat(arg.getSurname()).isEqualTo("Surname")));

  • Esta es una buena técnica. Sin embargo, actualmente obtengo un resultado bastante críptico: “Se busca pero no se invoca: /n mockAppender.append( );” – el argumento hay un CharSequence. ¿Conoce alguna forma de hacer que el informe imprima correctamente el argumento “buscado”?

    – mike roedor

    26 de febrero de 2017 a las 16:39

  • @mikerodent La salida críptica se puede arreglar si sigue la ruta más detallada de crear una clase que implemente ArgumentMatcher. Anular el método toString en su implementación proporcionará cualquier mensaje que desee en la salida de prueba de mockito.

    – Noé Salomón

    26/09/2019 a las 19:25

avatar de usuario
lector

Si no desea validar todas las llamadas a doSomething()solo el último, puedes usar ArgumentCaptor.getValue(). De acuerdo con la javadoc Mockito:

Si el método se llamó varias veces, devuelve el último valor capturado

Así que esto funcionaría (supone Foo tiene un método getName()):

ArgumentCaptor<Foo> fooCaptor = ArgumentCaptor.forClass(Foo.class);
verify(mockBar, times(2)).doSomething(fooCaptor.capture());
//getValue() contains value set in second call to doSomething()
assertEquals("2nd one", fooCaptor.getValue().getName());

  • ¿Hay alguna forma de capturar ambos valores?

    – Hars

    11 de junio de 2018 a las 6:40

avatar de usuario
Michał Stochmal

También puede usar @Captor ArgumentCaptor anotado. Por ejemplo:

@Mock
List<String> mockedList;

@Captor
ArgumentCaptor<String> argCaptor;

@BeforeTest
public void init() {
    //Initialize objects annotated with @Mock, @Captor and @Spy.
    MockitoAnnotations.initMocks(this);
}

@Test
public void shouldCallAddMethodTwice() {
    mockedList.add("one");
    mockedList.add("two");
    Mockito.verify(mockedList, times(2)).add(argCaptor.capture());

    assertEquals("one", argCaptor.getAllValues().get(0));
    assertEquals("two", argCaptor.getAllValues().get(1));
}

avatar de usuario
anton seredkin

Con las lambdas de Java 8, una forma conveniente es usar

org.mockito.invocation.InvocationOnMock

when(client.deleteByQuery(anyString(), anyString())).then(invocationOnMock -> {
    assertEquals("myCollection", invocationOnMock.getArgument(0));
    assertThat(invocationOnMock.getArgument(1), Matchers.startsWith("id:"));
}

  • No puedo ver cómo esto es más conveniente que la forma anterior. Me encanta el buen uso de lambdas, pero no estoy seguro de si este es uno.

    –Eric Wilson

    15 oct 2018 a las 17:04

avatar de usuario
fluir

En primer lugar: siempre debe importar mockito static, de esta manera el código será mucho más legible (e intuitivo); los ejemplos de código a continuación requieren que funcione:

import static org.mockito.Mockito.*;

En el método de verificar () puede pasar el ArgumentCaptor para asegurar la ejecución en la prueba y el ArgumentCaptor para evaluar los argumentos:

ArgumentCaptor<MyExampleClass> argument = ArgumentCaptor.forClass(MyExampleClass.class);
verify(yourmock, atleast(2)).myMethod(argument.capture());

List<MyExampleClass> passedArguments = argument.getAllValues();

for (MyExampleClass data : passedArguments){
    //assertSometing ...
    System.out.println(data.getFoo());
}

Se puede acceder a la lista de todos los argumentos pasados ​​durante su prueba a través del método argument.getAllValues().

Se puede acceder al valor del argumento único (último llamado) a través de argument.getValue() para una mayor manipulación/comprobación o lo que desee hacer.

  • No puedo ver cómo esto es más conveniente que la forma anterior. Me encanta el buen uso de lambdas, pero no estoy seguro de si este es uno.

    –Eric Wilson

    15 oct 2018 a las 17:04

¿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