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.