Learnitweb

Mockito ArgumentCaptor for Multiple Calls

In this tutorial, we’ll explore a slightly more advanced use case in Mockito: capturing arguments when a method is called multiple times. This is especially useful when you want to validate that a method was called with different arguments on each call.

1. Problem Statement

Suppose you have a mock object, and a method on this mock is invoked multiple times with different arguments. How can you capture all those arguments to validate them?

Mockito provides a powerful tool for this: ArgumentCaptor. Let’s see how we can use it to capture arguments across multiple method invocations.

2. Step-by-Step Example

Suppose we have the following scenario:

mock.add("SomeString1");
mock.add("SomeString2");

We want to capture both of these arguments and assert their values.

@Mock
List<String> mock;

@Captor
ArgumentCaptor<String> captor;

@Test
void multipleArgumentCapturing() {
    mock.add("SomeString1");
    mock.add("SomeString2");

    verify(mock, times(2)).add(captor.capture());

    List<String> allValues = captor.getAllValues();

    assertEquals("SomeString1", allValues.get(0));
    assertEquals("SomeString2", allValues.get(1));
}

Explanation

  • captor.capture() is used in the verify() call to intercept the arguments.
  • times(2) tells Mockito that the method should have been called exactly two times.
  • captor.getAllValues() retrieves all arguments that were passed during those calls.
  • We then assert that the captured values match what we expected.

3. Common Pitfall: Single Invocation Assumption

If you forget to specify times(2) in the verification step, Mockito will assume the method was called only once. This will lead to a test failure because the actual number of invocations differs from the expected.

So remember:

verify(mock).add(captor.capture()); // Assumes only 1 call

Should be:

verify(mock, times(2)).add(captor.capture()); // For multiple calls

4. Real-World Usage Tip

In this simple example, we’re directly calling the mock methods. However, in real-world applications, method calls typically happen inside a business method. For instance:

service.processData(Arrays.asList(1, 2, 3, 4, 5));

Inside processData(), your code might compute the sum of the list and call:

mock.saveSum(15);

In such cases, you’re sending raw data to the method under test, but capturing and verifying the transformed result passed to a mock.