CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-org-mockito--mockito-all

A comprehensive Java mocking framework that enables developers to create test doubles for unit testing.

Pending
Overview
Eval results
Files

argument-capturing.mddocs/

Argument Capturing

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.

Basic Argument Capturing

ArgumentCaptor Class

public class ArgumentCaptor<T> {
    public static <T> ArgumentCaptor<T> forClass(Class<T> clazz);
    public T capture();
    public T getValue();
    public List<T> getAllValues();
}

Creating ArgumentCaptors

// 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;

Single Value Capturing

Basic Usage

@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);
}

Capturing Complex Objects

@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());
}

Multiple Value Capturing

Capturing Multiple Invocations

@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));
}

Capturing Varargs

@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);
}

Advanced Capturing Patterns

Capturing with Verification Modes

@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());
}

Capturing Multiple Arguments

@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());
}

Capturing in Ordered Verification

@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());
}

Generic Type Capturing

Capturing Generic Collections

@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"));
}

Capturing with Wildcards

// 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
}

Integration with Matchers

Capturing with Argument Matchers

@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());
}

Conditional Capturing

@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;
    }));
}

Best Practices

When to Use ArgumentCaptor

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"));

Capturing vs Stubbing

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();

Error Handling

@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
    }
}

Captor Naming

// 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;

Common Patterns

Builder Pattern Verification

@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"));
}

Event Verification

@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());
}

Request/Response Validation

@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

docs

annotations.md

argument-capturing.md

bdd-testing.md

index.md

junit-integration.md

matchers.md

mock-creation.md

stubbing.md

verification.md

tile.json