0
# Argument Capturing
1
2
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.
3
4
## Basic Argument Capturing
5
6
### ArgumentCaptor Class
7
8
```java { .api }
9
public class ArgumentCaptor<T> {
10
public static <T> ArgumentCaptor<T> forClass(Class<T> clazz);
11
public T capture();
12
public T getValue();
13
public List<T> getAllValues();
14
}
15
```
16
17
### Creating ArgumentCaptors
18
19
```java
20
// Programmatic creation
21
ArgumentCaptor<String> stringCaptor = ArgumentCaptor.forClass(String.class);
22
ArgumentCaptor<User> userCaptor = ArgumentCaptor.forClass(User.class);
23
24
// Annotation-based creation (recommended)
25
@Captor
26
private ArgumentCaptor<String> stringCaptor;
27
28
@Captor
29
private ArgumentCaptor<User> userCaptor;
30
```
31
32
## Single Value Capturing
33
34
### Basic Usage
35
36
```java
37
@Mock
38
private EmailService emailService;
39
40
@Captor
41
private ArgumentCaptor<String> emailCaptor;
42
43
@Test
44
public void testEmailSending() {
45
UserService userService = new UserService(emailService);
46
47
// Execute the code under test
48
userService.registerUser("john@example.com", "John Doe");
49
50
// Capture the argument
51
verify(emailService).sendEmail(emailCaptor.capture());
52
53
// Assert on captured value
54
String capturedEmail = emailCaptor.getValue();
55
assertEquals("john@example.com", capturedEmail);
56
}
57
```
58
59
### Capturing Complex Objects
60
61
```java
62
@Mock
63
private UserRepository repository;
64
65
@Captor
66
private ArgumentCaptor<User> userCaptor;
67
68
@Test
69
public void testUserCreation() {
70
UserService service = new UserService(repository);
71
72
// Execute
73
service.createUser("John", "john@example.com", 25);
74
75
// Capture and verify
76
verify(repository).save(userCaptor.capture());
77
User capturedUser = userCaptor.getValue();
78
79
assertEquals("John", capturedUser.getName());
80
assertEquals("john@example.com", capturedUser.getEmail());
81
assertEquals(25, capturedUser.getAge());
82
}
83
```
84
85
## Multiple Value Capturing
86
87
### Capturing Multiple Invocations
88
89
```java
90
@Mock
91
private Logger logger;
92
93
@Captor
94
private ArgumentCaptor<String> messageCaptor;
95
96
@Test
97
public void testMultipleLogEntries() {
98
Service service = new Service(logger);
99
100
// Execute multiple operations that log
101
service.processItems(Arrays.asList("item1", "item2", "item3"));
102
103
// Capture all log messages
104
verify(logger, times(3)).log(messageCaptor.capture());
105
List<String> capturedMessages = messageCaptor.getAllValues();
106
107
assertEquals(3, capturedMessages.size());
108
assertEquals("Processing item1", capturedMessages.get(0));
109
assertEquals("Processing item2", capturedMessages.get(1));
110
assertEquals("Processing item3", capturedMessages.get(2));
111
}
112
```
113
114
### Capturing Varargs
115
116
```java
117
@Mock
118
private EventPublisher publisher;
119
120
@Captor
121
private ArgumentCaptor<Event> eventCaptor;
122
123
@Test
124
public void testEventPublishing() {
125
Service service = new Service(publisher);
126
127
// Method that publishes multiple events via varargs
128
service.processTransaction();
129
130
// Capture varargs
131
verify(publisher).publish(eventCaptor.capture());
132
List<Event> capturedEvents = eventCaptor.getAllValues();
133
134
assertEquals(2, capturedEvents.size());
135
assertTrue(capturedEvents.get(0) instanceof TransactionStartedEvent);
136
assertTrue(capturedEvents.get(1) instanceof TransactionCompletedEvent);
137
}
138
```
139
140
## Advanced Capturing Patterns
141
142
### Capturing with Verification Modes
143
144
```java
145
@Mock
146
private AuditService auditService;
147
148
@Captor
149
private ArgumentCaptor<AuditEvent> auditCaptor;
150
151
@Test
152
public void testAuditingWithTimeout() {
153
AsyncService service = new AsyncService(auditService);
154
155
// Execute async operation
156
service.performAsyncOperation();
157
158
// Capture with timeout for async operations
159
verify(auditService, timeout(1000)).logEvent(auditCaptor.capture());
160
AuditEvent capturedEvent = auditCaptor.getValue();
161
162
assertNotNull(capturedEvent);
163
assertEquals("ASYNC_OPERATION", capturedEvent.getType());
164
}
165
```
166
167
### Capturing Multiple Arguments
168
169
```java
170
@Mock
171
private NotificationService notificationService;
172
173
@Captor
174
private ArgumentCaptor<String> recipientCaptor;
175
176
@Captor
177
private ArgumentCaptor<String> messageCaptor;
178
179
@Test
180
public void testNotificationSending() {
181
Service service = new Service(notificationService);
182
183
service.sendNotification("user@example.com", "Your order is ready");
184
185
verify(notificationService).send(recipientCaptor.capture(), messageCaptor.capture());
186
187
assertEquals("user@example.com", recipientCaptor.getValue());
188
assertEquals("Your order is ready", messageCaptor.getValue());
189
}
190
```
191
192
### Capturing in Ordered Verification
193
194
```java
195
@Mock
196
private DatabaseService database;
197
198
@Mock
199
private CacheService cache;
200
201
@Captor
202
private ArgumentCaptor<String> dbKeyCaptor;
203
204
@Captor
205
private ArgumentCaptor<String> cacheKeyCaptor;
206
207
@Test
208
public void testOrderedOperations() {
209
Service service = new Service(database, cache);
210
211
service.updateData("key123", "newValue");
212
213
InOrder inOrder = inOrder(database, cache);
214
inOrder.verify(database).update(dbKeyCaptor.capture(), any());
215
inOrder.verify(cache).invalidate(cacheKeyCaptor.capture());
216
217
assertEquals("key123", dbKeyCaptor.getValue());
218
assertEquals("key123", cacheKeyCaptor.getValue());
219
}
220
```
221
222
## Generic Type Capturing
223
224
### Capturing Generic Collections
225
226
```java
227
@Mock
228
private DataProcessor processor;
229
230
@Captor
231
private ArgumentCaptor<List<String>> listCaptor;
232
233
@Captor
234
private ArgumentCaptor<Map<String, Object>> mapCaptor;
235
236
@Test
237
public void testGenericCapturing() {
238
Service service = new Service(processor);
239
240
service.processData();
241
242
verify(processor).process(listCaptor.capture(), mapCaptor.capture());
243
244
List<String> capturedList = listCaptor.getValue();
245
Map<String, Object> capturedMap = mapCaptor.getValue();
246
247
assertFalse(capturedList.isEmpty());
248
assertTrue(capturedMap.containsKey("timestamp"));
249
}
250
```
251
252
### Capturing with Wildcards
253
254
```java
255
// For methods that accept wildcard generics
256
@Captor
257
private ArgumentCaptor<List<? extends Serializable>> wildcardCaptor;
258
259
@Test
260
public void testWildcardCapturing() {
261
verify(service).process(wildcardCaptor.capture());
262
List<? extends Serializable> captured = wildcardCaptor.getValue();
263
// Process captured list
264
}
265
```
266
267
## Integration with Matchers
268
269
### Capturing with Argument Matchers
270
271
```java
272
@Mock
273
private EmailService emailService;
274
275
@Captor
276
private ArgumentCaptor<EmailMessage> messageCaptor;
277
278
@Test
279
public void testEmailWithMatchers() {
280
service.sendEmail();
281
282
// Mix capturing with other matchers
283
verify(emailService).send(
284
messageCaptor.capture(),
285
eq(Priority.HIGH),
286
any(DeliveryOptions.class)
287
);
288
289
EmailMessage captured = messageCaptor.getValue();
290
assertEquals("Important Update", captured.getSubject());
291
}
292
```
293
294
### Conditional Capturing
295
296
```java
297
@Test
298
public void testConditionalCapturing() {
299
service.processRequests();
300
301
// Capture only for specific conditions
302
verify(processor).handle(argThat(request -> {
303
if (request.getPriority() == Priority.HIGH) {
304
// Additional verification logic
305
return true;
306
}
307
return false;
308
}));
309
}
310
```
311
312
## Best Practices
313
314
### When to Use ArgumentCaptor
315
316
**Good use cases:**
317
```java
318
// Complex object verification
319
verify(service).save(userCaptor.capture());
320
User user = userCaptor.getValue();
321
assertEquals("expected@email.com", user.getEmail());
322
323
// Multiple invocation analysis
324
verify(logger, times(3)).log(messageCaptor.capture());
325
List<String> messages = messageCaptor.getAllValues();
326
assertTrue(messages.contains("Expected log message"));
327
328
// Dynamic value verification
329
verify(service).schedule(taskCaptor.capture());
330
Task task = taskCaptor.getValue();
331
assertTrue(task.getScheduledTime().isAfter(Instant.now()));
332
```
333
334
**Avoid when simple matchers suffice:**
335
```java
336
// Unnecessary - use matchers instead
337
verify(service).process(stringCaptor.capture());
338
assertEquals("expected", stringCaptor.getValue());
339
340
// Better
341
verify(service).process(eq("expected"));
342
```
343
344
### Capturing vs Stubbing
345
346
**Use ArgumentCaptor for verification, not stubbing:**
347
348
```java
349
// WRONG - don't use captors in stubbing
350
when(service.process(captor.capture())).thenReturn("result");
351
352
// CORRECT - use captors in verification
353
verify(service).process(captor.capture());
354
String captured = captor.getValue();
355
```
356
357
### Error Handling
358
359
```java
360
@Test
361
public void testCaptorErrorHandling() {
362
// Verify method was called before getting value
363
verify(service).process(captor.capture());
364
365
// Safe to get value after verification
366
String captured = captor.getValue();
367
368
// Check for multiple values
369
if (captor.getAllValues().size() > 1) {
370
// Handle multiple captures
371
List<String> allValues = captor.getAllValues();
372
// Process all values
373
}
374
}
375
```
376
377
### Captor Naming
378
379
```java
380
// Good - descriptive names
381
@Captor private ArgumentCaptor<User> userCaptor;
382
@Captor private ArgumentCaptor<EmailMessage> emailMessageCaptor;
383
@Captor private ArgumentCaptor<AuditEvent> auditEventCaptor;
384
385
// Avoid - generic names
386
@Captor private ArgumentCaptor<Object> objectCaptor;
387
@Captor private ArgumentCaptor<String> captor1;
388
```
389
390
## Common Patterns
391
392
### Builder Pattern Verification
393
394
```java
395
@Captor
396
private ArgumentCaptor<QueryBuilder> queryCaptor;
397
398
@Test
399
public void testQueryBuilding() {
400
service.findUsers("active", "admin");
401
402
verify(repository).findBy(queryCaptor.capture());
403
QueryBuilder query = queryCaptor.getValue();
404
405
assertTrue(query.hasCondition("status", "active"));
406
assertTrue(query.hasCondition("role", "admin"));
407
}
408
```
409
410
### Event Verification
411
412
```java
413
@Captor
414
private ArgumentCaptor<DomainEvent> eventCaptor;
415
416
@Test
417
public void testEventPublishing() {
418
service.updateUser(userId, newData);
419
420
verify(eventPublisher).publish(eventCaptor.capture());
421
DomainEvent event = eventCaptor.getValue();
422
423
assertEquals("UserUpdated", event.getType());
424
assertEquals(userId, event.getAggregateId());
425
}
426
```
427
428
### Request/Response Validation
429
430
```java
431
@Captor
432
private ArgumentCaptor<HttpRequest> requestCaptor;
433
434
@Test
435
public void testHttpCall() {
436
service.callExternalApi();
437
438
verify(httpClient).execute(requestCaptor.capture());
439
HttpRequest request = requestCaptor.getValue();
440
441
assertEquals("POST", request.getMethod());
442
assertEquals("/api/users", request.getPath());
443
assertNotNull(request.getHeader("Authorization"));
444
}
445
```