0
# Output Capture
1
2
Console output testing utilities for capturing and asserting on system output during tests, providing comprehensive testing of application logging and console output.
3
4
## Capabilities
5
6
### Captured Output Interface
7
8
Interface for accessing captured console output from tests.
9
10
```java { .api }
11
/**
12
* Interface for captured output from System.out and System.err
13
* @since 2.2.0
14
*/
15
public interface CapturedOutput {
16
17
/**
18
* Get the captured output written to System.out
19
*/
20
String getOut();
21
22
/**
23
* Get the captured output written to System.err
24
*/
25
String getErr();
26
27
/**
28
* Get all captured output (both out and err combined)
29
*/
30
String getAll();
31
32
/**
33
* String representation of all captured output
34
*/
35
@Override
36
String toString();
37
}
38
```
39
40
### JUnit Jupiter Extension
41
42
JUnit Jupiter extension for output capture integration.
43
44
```java { .api }
45
/**
46
* JUnit Jupiter extension for capturing System.out and System.err output
47
* @since 2.2.0
48
*/
49
public class OutputCaptureExtension implements BeforeEachCallback, AfterEachCallback, ParameterResolver {
50
51
/**
52
* Set up output capture before each test method
53
*/
54
@Override
55
public void beforeEach(ExtensionContext context) throws Exception;
56
57
/**
58
* Clean up output capture after each test method
59
*/
60
@Override
61
public void afterEach(ExtensionContext context) throws Exception;
62
63
/**
64
* Resolve CapturedOutput parameter for test methods
65
*/
66
@Override
67
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext);
68
69
/**
70
* Provide CapturedOutput instance for test methods
71
*/
72
@Override
73
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext);
74
}
75
```
76
77
**Usage Examples:**
78
79
```java
80
@ExtendWith(OutputCaptureExtension.class)
81
class OutputCaptureJupiterTest {
82
83
@Test
84
void captureOutput(CapturedOutput output) {
85
System.out.println("Hello World");
86
System.err.println("Error message");
87
88
assertThat(output.getOut()).contains("Hello World");
89
assertThat(output.getErr()).contains("Error message");
90
assertThat(output.getAll()).contains("Hello World", "Error message");
91
}
92
93
@Test
94
void captureLoggingOutput(CapturedOutput output) {
95
Logger logger = LoggerFactory.getLogger(OutputCaptureJupiterTest.class);
96
logger.info("This is an info message");
97
logger.warn("This is a warning message");
98
logger.error("This is an error message");
99
100
assertThat(output.getAll())
101
.contains("This is an info message")
102
.contains("This is a warning message")
103
.contains("This is an error message");
104
}
105
106
@Test
107
void captureSystemPropertyOutput(CapturedOutput output) {
108
System.setProperty("test.property", "test-value");
109
System.out.println("Property value: " + System.getProperty("test.property"));
110
111
assertThat(output.getOut()).contains("Property value: test-value");
112
}
113
}
114
```
115
116
### JUnit 4 Rule
117
118
JUnit 4 rule for output capture (for legacy test support).
119
120
```java { .api }
121
/**
122
* JUnit 4 rule for capturing System.out and System.err output
123
* @since 1.4.0
124
*/
125
public class OutputCaptureRule implements TestRule {
126
127
/**
128
* Create an OutputCaptureRule
129
*/
130
public OutputCaptureRule();
131
132
/**
133
* Apply the rule to the given test
134
*/
135
@Override
136
public Statement apply(Statement base, Description description);
137
138
/**
139
* Get the captured output written to System.out
140
*/
141
public String getOut();
142
143
/**
144
* Get the captured output written to System.err
145
*/
146
public String getErr();
147
148
/**
149
* Get all captured output (both out and err combined)
150
*/
151
public String getAll();
152
153
/**
154
* String representation of all captured output
155
*/
156
@Override
157
public String toString();
158
}
159
```
160
161
**Usage Examples:**
162
163
```java
164
public class OutputCaptureRuleTest {
165
166
@Rule
167
public OutputCaptureRule output = new OutputCaptureRule();
168
169
@Test
170
public void captureOutput() {
171
System.out.println("Hello from JUnit 4");
172
System.err.println("Error from JUnit 4");
173
174
assertThat(output.getOut()).contains("Hello from JUnit 4");
175
assertThat(output.getErr()).contains("Error from JUnit 4");
176
assertThat(output.getAll()).contains("Hello from JUnit 4", "Error from JUnit 4");
177
}
178
}
179
```
180
181
### Integration with Spring Boot Tests
182
183
Output capture integration with Spring Boot testing annotations.
184
185
**Usage Examples:**
186
187
```java
188
@SpringBootTest
189
@ExtendWith(OutputCaptureExtension.class)
190
class SpringBootOutputCaptureTest {
191
192
@Autowired
193
private MyService myService;
194
195
@Test
196
void captureServiceOutput(CapturedOutput output) {
197
myService.performAction();
198
199
assertThat(output.getAll())
200
.contains("Service action started")
201
.contains("Service action completed");
202
}
203
204
@Test
205
void captureStartupOutput(CapturedOutput output) {
206
// Output capture will include Spring Boot startup messages
207
assertThat(output.getAll())
208
.contains("Started SpringBootOutputCaptureTest")
209
.contains("Spring Boot");
210
}
211
}
212
213
@WebMvcTest(UserController.class)
214
@ExtendWith(OutputCaptureExtension.class)
215
class WebMvcOutputCaptureTest {
216
217
@Autowired
218
private MockMvc mockMvc;
219
220
@MockBean
221
private UserService userService;
222
223
@Test
224
void captureControllerOutput(CapturedOutput output) throws Exception {
225
when(userService.findByEmail("test@example.com"))
226
.thenReturn(new User("test@example.com", "Test User"));
227
228
mockMvc.perform(get("/users/test@example.com"))
229
.andExpect(status().isOk());
230
231
// Verify that controller logging is captured
232
assertThat(output.getAll()).contains("Processing user request");
233
}
234
}
235
```
236
237
### Advanced Output Capture Patterns
238
239
Advanced patterns for testing complex output scenarios.
240
241
**Usage Examples:**
242
243
```java
244
@ExtendWith(OutputCaptureExtension.class)
245
class AdvancedOutputCaptureTest {
246
247
@Test
248
void captureMultiThreadedOutput(CapturedOutput output) throws InterruptedException {
249
ExecutorService executor = Executors.newFixedThreadPool(3);
250
CountDownLatch latch = new CountDownLatch(3);
251
252
for (int i = 0; i < 3; i++) {
253
final int threadNum = i;
254
executor.submit(() -> {
255
try {
256
System.out.println("Thread " + threadNum + " starting");
257
Thread.sleep(100);
258
System.out.println("Thread " + threadNum + " finished");
259
} catch (InterruptedException e) {
260
Thread.currentThread().interrupt();
261
} finally {
262
latch.countDown();
263
}
264
});
265
}
266
267
latch.await(5, TimeUnit.SECONDS);
268
executor.shutdown();
269
270
String allOutput = output.getAll();
271
assertThat(allOutput)
272
.contains("Thread 0 starting", "Thread 0 finished")
273
.contains("Thread 1 starting", "Thread 1 finished")
274
.contains("Thread 2 starting", "Thread 2 finished");
275
}
276
277
@Test
278
void captureExceptionOutput(CapturedOutput output) {
279
try {
280
throw new RuntimeException("Test exception with cause",
281
new IllegalArgumentException("Root cause"));
282
} catch (Exception e) {
283
e.printStackTrace();
284
}
285
286
String errOutput = output.getErr();
287
assertThat(errOutput)
288
.contains("RuntimeException: Test exception with cause")
289
.contains("Caused by: java.lang.IllegalArgumentException: Root cause")
290
.contains("at " + getClass().getName());
291
}
292
293
@Test
294
void captureProgressOutput(CapturedOutput output) {
295
System.out.print("Processing");
296
for (int i = 0; i < 5; i++) {
297
System.out.print(".");
298
try {
299
Thread.sleep(50);
300
} catch (InterruptedException e) {
301
Thread.currentThread().interrupt();
302
}
303
}
304
System.out.println(" Done!");
305
306
assertThat(output.getOut())
307
.contains("Processing.....")
308
.contains("Done!");
309
}
310
311
@Test
312
void captureFormattedOutput(CapturedOutput output) {
313
String name = "Alice";
314
int age = 30;
315
double salary = 75000.50;
316
317
System.out.printf("Employee: %s, Age: %d, Salary: $%.2f%n", name, age, salary);
318
System.out.println(String.format("Formatted data: [%10s] [%3d] [%10.2f]", name, age, salary));
319
320
assertThat(output.getOut())
321
.contains("Employee: Alice, Age: 30, Salary: $75000.50")
322
.contains("Formatted data: [ Alice] [ 30] [ 75000.50]");
323
}
324
325
@Test
326
void captureConditionalOutput(CapturedOutput output) {
327
boolean debugEnabled = true;
328
String operation = "data-processing";
329
330
System.out.println("Starting operation: " + operation);
331
332
if (debugEnabled) {
333
System.out.println("DEBUG: Operation details logged");
334
System.err.println("DEBUG: Memory usage checked");
335
}
336
337
System.out.println("Operation completed successfully");
338
339
assertThat(output.getOut())
340
.contains("Starting operation: data-processing")
341
.contains("DEBUG: Operation details logged")
342
.contains("Operation completed successfully");
343
344
assertThat(output.getErr())
345
.contains("DEBUG: Memory usage checked");
346
}
347
348
@Test
349
void captureNoOutput(CapturedOutput output) {
350
// Test that runs without producing any output
351
int result = 2 + 2;
352
353
assertThat(output.getOut()).isEmpty();
354
assertThat(output.getErr()).isEmpty();
355
assertThat(output.getAll()).isEmpty();
356
assertThat(result).isEqualTo(4);
357
}
358
359
@Test
360
void captureAndAnalyzeLogLevels(CapturedOutput output) {
361
Logger logger = LoggerFactory.getLogger("com.example.TestLogger");
362
363
logger.trace("This is a trace message");
364
logger.debug("This is a debug message");
365
logger.info("This is an info message");
366
logger.warn("This is a warning message");
367
logger.error("This is an error message");
368
369
String allOutput = output.getAll();
370
371
// Verify log levels are captured (depending on logging configuration)
372
assertThat(allOutput).contains("INFO");
373
assertThat(allOutput).contains("WARN");
374
assertThat(allOutput).contains("ERROR");
375
376
// Verify actual log messages
377
assertThat(allOutput)
378
.contains("This is an info message")
379
.contains("This is a warning message")
380
.contains("This is an error message");
381
}
382
}
383
384
// Example service that produces output for testing
385
@Service
386
static class MyService {
387
private static final Logger logger = LoggerFactory.getLogger(MyService.class);
388
389
public void performAction() {
390
logger.info("Service action started");
391
System.out.println("Performing business logic");
392
393
try {
394
Thread.sleep(100);
395
} catch (InterruptedException e) {
396
Thread.currentThread().interrupt();
397
}
398
399
logger.info("Service action completed");
400
System.out.println("Action finished successfully");
401
}
402
}
403
```
404
405
### Best Practices for Output Capture
406
407
Guidelines for effective output capture testing:
408
409
1. **Test Specific Output**: Focus on testing specific, meaningful output rather than implementation details
410
2. **Use Appropriate Assertions**: Use `contains()` for partial matches, `isEqualTo()` for exact matches
411
3. **Handle Timing**: Be aware of timing issues in multi-threaded scenarios
412
4. **Separate Concerns**: Test output separately from business logic when possible
413
5. **Consider Log Levels**: Remember that captured output depends on logging configuration
414
415
**Usage Examples:**
416
417
```java
418
@ExtendWith(OutputCaptureExtension.class)
419
class OutputCaptureBestPracticesTest {
420
421
@Test
422
void testSpecificBusinessOutput(CapturedOutput output) {
423
BusinessService service = new BusinessService();
424
service.processOrder("ORDER-123");
425
426
// Test specific business-relevant output
427
assertThat(output.getAll())
428
.contains("Processing order: ORDER-123")
429
.contains("Order processed successfully");
430
}
431
432
@Test
433
void testErrorScenarioOutput(CapturedOutput output) {
434
BusinessService service = new BusinessService();
435
436
assertThatThrownBy(() -> service.processOrder(null))
437
.isInstanceOf(IllegalArgumentException.class);
438
439
// Verify error logging
440
assertThat(output.getErr())
441
.contains("Invalid order ID: null");
442
}
443
444
@Test
445
void testOutputWithRegex(CapturedOutput output) {
446
BusinessService service = new BusinessService();
447
service.generateReport();
448
449
// Use regex for flexible matching
450
assertThat(output.getAll())
451
.containsPattern("Report generated at \\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}")
452
.containsPattern("Total records: \\d+");
453
}
454
}
455
```