Capturing Method Arguments with ArgumentCaptor

Illustration for Capturing Method Arguments with ArgumentCaptor
By Last updated:

When testing real-world applications, we often need to verify not just whether a method was called, but also what arguments were passed to it. For example, you may want to check that a service correctly passed a constructed object to a repository.

This is where Mockito’s ArgumentCaptor comes in. It allows you to capture arguments passed to a mocked method and then assert their properties.


What is ArgumentCaptor?

ArgumentCaptor is a powerful Mockito utility for capturing and asserting method call arguments. Instead of only verifying that a method was called, you can inspect the actual values passed during execution.


Example: Capturing Arguments

Business Classes

class User {
    private String name;
    User(String name) { this.name = name; }
    public String getName() { return name; }
}

interface UserRepository {
    void save(User user);
}

class UserService {
    private final UserRepository repository;
    UserService(UserRepository repository) { this.repository = repository; }

    void registerUser(String name) {
        repository.save(new User(name));
    }
}

Test with ArgumentCaptor

@ExtendWith(MockitoExtension.class)
class UserServiceTest {

    @Mock
    private UserRepository repository;

    @InjectMocks
    private UserService service;

    @Test
    void shouldCaptureArgument() {
        ArgumentCaptor<User> captor = ArgumentCaptor.forClass(User.class);

        service.registerUser("Alice");

        verify(repository).save(captor.capture());
        assertEquals("Alice", captor.getValue().getName());
    }
}

Here, ArgumentCaptor captures the User passed to repository.save(), allowing us to assert its state.


Capturing Multiple Arguments

@Test
void shouldCaptureMultipleArguments() {
    ArgumentCaptor<User> captor = ArgumentCaptor.forClass(User.class);

    service.registerUser("Alice");
    service.registerUser("Bob");

    verify(repository, times(2)).save(captor.capture());

    List<User> capturedUsers = captor.getAllValues();
    assertEquals("Alice", capturedUsers.get(0).getName());
    assertEquals("Bob", capturedUsers.get(1).getName());
}

You can use getAllValues() to retrieve all captured arguments across multiple method calls.


Why ArgumentCaptor is Useful

  • Deep Verification: Go beyond checking method calls to validating passed data.
  • Readable Tests: Clear separation of verification and assertion.
  • Works with Complex Objects: Useful when arguments are dynamically constructed.

Analogy: Think of ArgumentCaptor as a camera—it captures a snapshot of what was passed into a method for later inspection.


Best Practices

  1. Use When Necessary — Prefer simpler verify() when possible.
  2. Avoid Overuse — Too many captures may indicate overly complex interactions.
  3. Combine with Matchers — For flexible and readable tests.
  4. Assert Behavior, Not Implementation — Don’t test every detail, only critical state.
  5. Leverage Parameterized Tests — Combine with JUnit 5 for broader coverage.

Mockito + ArgumentCaptor + Testcontainers Example

In integration-heavy apps, capture arguments in unit tests, then validate behavior with Testcontainers in integration tests:

@Test
void shouldStoreUserInDatabase() {
    try (PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")) {
        postgres.start();
        assertTrue(postgres.isRunning());
    }
}

This ensures mock verification is complemented with real-world validation.


Version Tracker

  • Mockito Improvements: Enhanced support for capturing generics.
  • JUnit 5 Transition: Full compatibility with @ExtendWith(MockitoExtension.class).
  • Testcontainers Growth: Parallel adoption for integration tests.

Conclusion & Key Takeaways

Mockito’s ArgumentCaptor is essential when verifying the exact data passed into mocks.

Key Takeaways:

  • Use ArgumentCaptor to capture and assert method arguments.
  • Supports single and multiple argument captures.
  • Best for validating stateful objects passed into dependencies.
  • Combine with Testcontainers for full pipeline validation.

FAQ

1. What is ArgumentCaptor in Mockito?
A utility for capturing and inspecting method arguments passed to mocks.

2. How do I use ArgumentCaptor?
Create with ArgumentCaptor.forClass(), call capture() inside verify(), and use getValue() or getAllValues().

3. Can I capture multiple arguments?
Yes, use getAllValues().

4. Should I always use ArgumentCaptor?
No, only when you need to inspect arguments.

5. How is it different from matchers like eq()?
Matchers check expected values; captors capture the actual runtime values.

6. Can I use ArgumentCaptor with generics?
Yes, but you may need to provide type tokens.

7. Does ArgumentCaptor replace verify()?
No, it complements verify() by allowing argument inspection.

8. Can I use ArgumentCaptor with spies?
Yes, it works with both mocks and spies.

9. How does it help in CI/CD?
Improves test reliability by ensuring correct data flow.

10. What’s a common misuse of ArgumentCaptor?
Over-capturing and verifying trivial details instead of focusing on business logic.