A comprehensive Java mocking framework that enables developers to create test doubles for unit testing.
—
ArgumentCaptor allows you to capture arguments passed to mock methods during verification. This is useful for making detailed assertions on complex objects or when you need to inspect the actual values passed to mocks.
public class ArgumentCaptor<T> {
public static <T> ArgumentCaptor<T> forClass(Class<T> clazz);
public T capture();
public T getValue();
public List<T> getAllValues();
}// Programmatic creation
ArgumentCaptor<String> stringCaptor = ArgumentCaptor.forClass(String.class);
ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
// Annotation-based creation (recommended)
@Captor
private ArgumentCaptor<String> stringCaptor;
@Captor
private ArgumentCaptor<User> userCaptor;@Mock
private EmailService emailService;
@Captor
private ArgumentCaptor<String> emailCaptor;
@Test
public void testEmailSending() {
UserService userService = new UserService(emailService);
// Execute the code under test
userService.registerUser("john@example.com", "John Doe");
// Capture the argument
verify(emailService).sendEmail(emailCaptor.capture());
// Assert on captured value
String capturedEmail = emailCaptor.getValue();
assertEquals("john@example.com", capturedEmail);
}@Mock
private UserRepository repository;
@Captor
private ArgumentCaptor<User> userCaptor;
@Test
public void testUserCreation() {
UserService service = new UserService(repository);
// Execute
service.createUser("John", "john@example.com", 25);
// Capture and verify
verify(repository).save(userCaptor.capture());
User capturedUser = userCaptor.getValue();
assertEquals("John", capturedUser.getName());
assertEquals("john@example.com", capturedUser.getEmail());
assertEquals(25, capturedUser.getAge());
}@Mock
private Logger logger;
@Captor
private ArgumentCaptor<String> messageCaptor;
@Test
public void testMultipleLogEntries() {
Service service = new Service(logger);
// Execute multiple operations that log
service.processItems(Arrays.asList("item1", "item2", "item3"));
// Capture all log messages
verify(logger, times(3)).log(messageCaptor.capture());
List<String> capturedMessages = messageCaptor.getAllValues();
assertEquals(3, capturedMessages.size());
assertEquals("Processing item1", capturedMessages.get(0));
assertEquals("Processing item2", capturedMessages.get(1));
assertEquals("Processing item3", capturedMessages.get(2));
}@Mock
private EventPublisher publisher;
@Captor
private ArgumentCaptor<Event> eventCaptor;
@Test
public void testEventPublishing() {
Service service = new Service(publisher);
// Method that publishes multiple events via varargs
service.processTransaction();
// Capture varargs
verify(publisher).publish(eventCaptor.capture());
List<Event> capturedEvents = eventCaptor.getAllValues();
assertEquals(2, capturedEvents.size());
assertTrue(capturedEvents.get(0) instanceof TransactionStartedEvent);
assertTrue(capturedEvents.get(1) instanceof TransactionCompletedEvent);
}@Mock
private AuditService auditService;
@Captor
private ArgumentCaptor<AuditEvent> auditCaptor;
@Test
public void testAuditingWithTimeout() {
AsyncService service = new AsyncService(auditService);
// Execute async operation
service.performAsyncOperation();
// Capture with timeout for async operations
verify(auditService, timeout(1000)).logEvent(auditCaptor.capture());
AuditEvent capturedEvent = auditCaptor.getValue();
assertNotNull(capturedEvent);
assertEquals("ASYNC_OPERATION", capturedEvent.getType());
}@Mock
private NotificationService notificationService;
@Captor
private ArgumentCaptor<String> recipientCaptor;
@Captor
private ArgumentCaptor<String> messageCaptor;
@Test
public void testNotificationSending() {
Service service = new Service(notificationService);
service.sendNotification("user@example.com", "Your order is ready");
verify(notificationService).send(recipientCaptor.capture(), messageCaptor.capture());
assertEquals("user@example.com", recipientCaptor.getValue());
assertEquals("Your order is ready", messageCaptor.getValue());
}@Mock
private DatabaseService database;
@Mock
private CacheService cache;
@Captor
private ArgumentCaptor<String> dbKeyCaptor;
@Captor
private ArgumentCaptor<String> cacheKeyCaptor;
@Test
public void testOrderedOperations() {
Service service = new Service(database, cache);
service.updateData("key123", "newValue");
InOrder inOrder = inOrder(database, cache);
inOrder.verify(database).update(dbKeyCaptor.capture(), any());
inOrder.verify(cache).invalidate(cacheKeyCaptor.capture());
assertEquals("key123", dbKeyCaptor.getValue());
assertEquals("key123", cacheKeyCaptor.getValue());
}@Mock
private DataProcessor processor;
@Captor
private ArgumentCaptor<List<String>> listCaptor;
@Captor
private ArgumentCaptor<Map<String, Object>> mapCaptor;
@Test
public void testGenericCapturing() {
Service service = new Service(processor);
service.processData();
verify(processor).process(listCaptor.capture(), mapCaptor.capture());
List<String> capturedList = listCaptor.getValue();
Map<String, Object> capturedMap = mapCaptor.getValue();
assertFalse(capturedList.isEmpty());
assertTrue(capturedMap.containsKey("timestamp"));
}// For methods that accept wildcard generics
@Captor
private ArgumentCaptor<List<? extends Serializable>> wildcardCaptor;
@Test
public void testWildcardCapturing() {
verify(service).process(wildcardCaptor.capture());
List<? extends Serializable> captured = wildcardCaptor.getValue();
// Process captured list
}@Mock
private EmailService emailService;
@Captor
private ArgumentCaptor<EmailMessage> messageCaptor;
@Test
public void testEmailWithMatchers() {
service.sendEmail();
// Mix capturing with other matchers
verify(emailService).send(
messageCaptor.capture(),
eq(Priority.HIGH),
any(DeliveryOptions.class)
);
EmailMessage captured = messageCaptor.getValue();
assertEquals("Important Update", captured.getSubject());
}@Test
public void testConditionalCapturing() {
service.processRequests();
// Capture only for specific conditions
verify(processor).handle(argThat(request -> {
if (request.getPriority() == Priority.HIGH) {
// Additional verification logic
return true;
}
return false;
}));
}Good use cases:
// Complex object verification
verify(service).save(userCaptor.capture());
User user = userCaptor.getValue();
assertEquals("expected@email.com", user.getEmail());
// Multiple invocation analysis
verify(logger, times(3)).log(messageCaptor.capture());
List<String> messages = messageCaptor.getAllValues();
assertTrue(messages.contains("Expected log message"));
// Dynamic value verification
verify(service).schedule(taskCaptor.capture());
Task task = taskCaptor.getValue();
assertTrue(task.getScheduledTime().isAfter(Instant.now()));Avoid when simple matchers suffice:
// Unnecessary - use matchers instead
verify(service).process(stringCaptor.capture());
assertEquals("expected", stringCaptor.getValue());
// Better
verify(service).process(eq("expected"));Use ArgumentCaptor for verification, not stubbing:
// WRONG - don't use captors in stubbing
when(service.process(captor.capture())).thenReturn("result");
// CORRECT - use captors in verification
verify(service).process(captor.capture());
String captured = captor.getValue();@Test
public void testCaptorErrorHandling() {
// Verify method was called before getting value
verify(service).process(captor.capture());
// Safe to get value after verification
String captured = captor.getValue();
// Check for multiple values
if (captor.getAllValues().size() > 1) {
// Handle multiple captures
List<String> allValues = captor.getAllValues();
// Process all values
}
}// Good - descriptive names
@Captor private ArgumentCaptor<User> userCaptor;
@Captor private ArgumentCaptor<EmailMessage> emailMessageCaptor;
@Captor private ArgumentCaptor<AuditEvent> auditEventCaptor;
// Avoid - generic names
@Captor private ArgumentCaptor<Object> objectCaptor;
@Captor private ArgumentCaptor<String> captor1;@Captor
private ArgumentCaptor<QueryBuilder> queryCaptor;
@Test
public void testQueryBuilding() {
service.findUsers("active", "admin");
verify(repository).findBy(queryCaptor.capture());
QueryBuilder query = queryCaptor.getValue();
assertTrue(query.hasCondition("status", "active"));
assertTrue(query.hasCondition("role", "admin"));
}@Captor
private ArgumentCaptor<DomainEvent> eventCaptor;
@Test
public void testEventPublishing() {
service.updateUser(userId, newData);
verify(eventPublisher).publish(eventCaptor.capture());
DomainEvent event = eventCaptor.getValue();
assertEquals("UserUpdated", event.getType());
assertEquals(userId, event.getAggregateId());
}@Captor
private ArgumentCaptor<HttpRequest> requestCaptor;
@Test
public void testHttpCall() {
service.callExternalApi();
verify(httpClient).execute(requestCaptor.capture());
HttpRequest request = requestCaptor.getValue();
assertEquals("POST", request.getMethod());
assertEquals("/api/users", request.getPath());
assertNotNull(request.getHeader("Authorization"));
}Install with Tessl CLI
npx tessl i tessl/maven-org-mockito--mockito-all