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


Tengo un método 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 una TooManyActualInvocations Excepción, ya que Mockito piensa que doSomething solo debe ser llamado una vez.

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

Author: Eric Wilson, 2011-05-12

4 answers

Creo que debería ser

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

Muestra de mockito 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());
 601
Author: proactif,
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-10-23 10:32:12

Desde Mockito 2.0 también existe la posibilidad de usar Matchers de método estático .argThat (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ás atado a una versión de Java inferior, también hay algo no tan 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 InOrder :

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, echa un vistazo a mockito-java8 proyecto que hace posible hacer llamadas tales como:

verify(mockBar).doSth(assertArg(arg -> assertThat(arg.getSurname()).isEqualTo("Surname")));
 31
Author: Maciej Dobrowolski,
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-01-14 21:15:28

Si no desea validar todas las llamadas a doSomething(), solo la última, puede usar ArgumentCaptor.getValue(). Según el Mockito javadoc :

Si el método fue llamado varias veces, entonces devuelve el último valor capturado

Así que esto funcionaría (asume que 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());
 23
Author: lreeder,
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-08-22 14:56:48

También puedes usar @Captor annotated ArgumentCaptor. 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));
}
 6
Author: Michał Stochmal,
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-07-04 11:25:28