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() after verify. Otherwise Mockito may throw an IllegalStateException.
  • 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. @Captor can 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.