ArgumentCaptor Parameter Capture
Mockito's ArgumentCaptor is a support class that records the arguments supplied to a mock method during a verification step. It gives tests a way to inspect the actual values that were passed, rather than relying only on expected values declared in the test.
Why Captors Are Useful
Normal verification can check arguments directly:
verify(mock).method(eq(expectedValue));
This is clear and simple when the expected value is known. However, real-world tests often run into situations where direct matching is not practical:
- The argument is a complex object, so several fields need to be checked.
- The method is called more than once, and each call should be examined.
- The argument is created inside the method, making it hard to construct an equal expected instance upfront.
- The captured value may need further processing or additional assertions.
In these cases, ArgumentCaptor helps bridge the gap between verification and inspection.
Creating an ArgumentCaptor
You can create a captor with ArgumentCaptor.forClass:
ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
When using generic types, the class-based factory can produce an unchecked warning. The @Captor annotation is often preferred for cleaner test code.
Capturing and Verifying
Attach the captor to the method call inside verify:
verify(userRepository).save(userCaptor.capture());
User capturedUser = userCaptor.getValue();
assertEquals("Alice", capturedUser.getName());
The capture() method returns null. Its real job is to tell Mockito to record the argument. After verify, call getValue() for the latest captured argument or getAllValues() for a list of captured arguments.
Capturing Multiple Arguments
When a method accepts more than one parameter, create a captor for each argument:
ArgumentCaptor<String> nameCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<Integer> ageCaptor = ArgumentCaptor.forClass(Integer.class);
verify(service).process(nameCaptor.capture(), ageCaptor.capture());
assertEquals("Alice", nameCaptor.getValue());
assertEquals(30, ageCaptor.getValue());
Capturing Repeated Calls
For methods invoked several times, use getAllValues():
verify(repository, times(2)).save(userCaptor.capture());
List<User> users = userCaptor.getAllValues();
assertEquals(2, users.size());
Combining With ArgumentMatchers
An ArgumentCaptor is itself an argument matcher. You can use it alongside other matchers, but avoid combining capture() with matchers such as eq() for the same argument.
Practical Considerations
- Use captors when needed. If a simple
eq()matcher is enough, prefer direct verification. - Call
getValue()afterverify. Otherwise Mockito may throw anIllegalStateException. - Mind mutable state. The captor stores references. If the object changes later, the captured value may change too. Use defensive copies when necessary.
- Handle generics carefully.
@Captorcan reduce unchecked warnings. - Remember repeated calls.
getValue()returns the last captured value;getAllValues()returns all captured values.
Example
Suppose a UserService delegates saving to a UserRepository. The test wants to confirm the saved user has the expected name and age.
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User createUser(String name, int age) {
User user = new User(name, age);
return userRepository.save(user);
}
}
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Captor
private ArgumentCaptor<User> userCaptor;
@Test
void createUser_shouldSaveUserWithCorrectNameAndAge() {
when(userRepository.save(any(User.class)))
.thenAnswer(invocation -> invocation.getArgument(0));
userService.createUser("Alice", 30);
verify(userRepository).save(userCaptor.capture());
User capturedUser = userCaptor.getValue();
assertEquals("Alice", capturedUser.getName());
assertEquals(30, capturedUser.getAge());
}
}
Summary
ArgumentCaptor is a powerful Mockito tool for capturing method arguments. It fits scenarios involving complex objects, repeated invocations, or arguments that cannot easily be predicted in advance. Used well, it makes unit tests more precise. Used too often, it can hide simpler verification needs.