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