Can Mockito capture the parameters of a method that is called multiple times?

I have a method that is called twice, and I want to capture the parameters of the second method call.

This is what I tried:

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()

But I got a TooManyActualInvocations exception because Mockito thought doSomething should only be called once.

How to verify the parameters of the second call of doSomething?

#1 building

If you don't want to verify that doSomething() is the last call to doSomething(), you can use ArgumentCaptor.getValue(). according to Mockito javadoc :

If the method is called more than once, it will return the latest captured value

This is OK (assuming Foo has a method 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());

#2 building

Starting with Mockito 2.0, you can also use static methods Matchers.argThat(ArgumentMatcher) . With the help of Java 8, it's now much cleaner and more readable to write:

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

If you bind to a lower Java version, it's not that bad either:

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

Of course, none of this validates the call sequence - you should use the 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")));

Please have a look. Laugh at java8 Project, which can be called as follows:

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

#3 building

You can also use the ArgumentCaptor of the @ Captor annotation. For example:

@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));
}

#4 building

One convenient way to use Java 8's lambda is to use

org.mockito.invocation.InvocationOnMock

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

#5 building

First of all: you should always import static static so that the code will be more readable (intuitive) - the following code example requires it to work properly:

import static org.mockito.Mockito.*;

In the verify () method, you can pass ArgumentCaptor to ensure execution in the test, and ArgumentCaptor to evaluate parameters:

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());
}

You can access the list of all parameters passed during the test through the arguments.getAllValues () method.

The value of a single (last call) parameter can be accessed through arguments.getValue () to perform further operations / checks or to perform any operation you want to perform.

Keywords: Java Lambda

Added by Blesbok on Fri, 07 Feb 2020 16:45:19 +0200