Package rife.bld.extension.testing
Annotation Interface CaptureOutput
@Target({TYPE,METHOD})
@Retention(RUNTIME)
@ExtendWith(CaptureOutputExtension.class)
public @interface CaptureOutput
JUnit annotation to capture stdout and stderr output for specific test methods.
This annotation can be applied to individual test methods or entire test classes
to capture all console output (both System.out
and System.err
)
during test execution. The captured output is then made available through a
CapturedOutput
parameter that can be injected into the test method.
The annotation automatically handles the setup and teardown of output capture, ensuring that the original stdout and stderr streams are properly restored after each test execution. Additionally, it provides chronological tracking of output with timestamps and type information.
Basic Usage Example:
class MyTest { @Test @CaptureOutput void testConsoleOutput(CapturedOutput output) { System.out.println("Hello World"); System.err.println("Error message"); // Traditional stream-based access assertEquals("Hello World\n", output.getOut()); assertEquals("Error message\n", output.getErr()); assertTrue(output.contains("Hello")); } @Test void regularTest() { // This test runs normally without output capture System.out.println("Goes to console"); } }
Chronological Tracking Example:
@Test @CaptureOutput void testChronologicalOutput(CapturedOutput output) { System.out.print("First stdout"); System.err.print("First stderr"); System.out.println(" - Second stdout"); // Access output in exact chronological order String chronological = output.getChronologicalContent(); assertEquals("First stdoutFirst stderr - Second stdout\n", chronological); // Access individual entries with timestamps and types List<CapturedOutput.OutputEntry> entries = output.getChronologicalEntries(); assertEquals(3, entries.size()); assertEquals(CapturedOutput.OutputType.STDOUT, entries.get(0).getType()); assertEquals("First stdout", entries.get(0).getContent()); assertTrue(entries.get(0).getTimestamp().isBefore(entries.get(1).getTimestamp())); }
Class-Level Usage:
@CaptureOutput class MyTestClass { @Test void testOne(CapturedOutput output) { // This test has output capture enabled System.out.println("Test one output"); assertFalse(output.isEmpty()); } @Test void testTwo(CapturedOutput output) { // This test also has output capture enabled System.err.println("Test two error"); assertTrue(output.errContains("error")); } }
Key Features:
- Targeted capture - Only applies to annotated test methods or classes
- Automatic cleanup - Restores original streams after test execution
- Parameter injection - Provides
CapturedOutput
for analysis - Thread-safe - Each test gets its own capture instance
- Chronological tracking - Records output order, timestamps, and stream types
- Multiple access patterns - Stream-grouped or chronologically ordered access
Output Access Methods:
- Stream-grouped access:
CapturedOutput.getOut()
- All stdout contentCapturedOutput.getErr()
- All stderr contentCapturedOutput.getAll()
- Stdout followed by stderr
- Chronological access:
CapturedOutput.getChronologicalContent()
- Output in exact order of occurrenceCapturedOutput.getChronologicalEntries()
- Individual entries with metadataCapturedOutput.getChronologicalLines()
- Lines in chronological order
- Line-based access:
CapturedOutput.getOutLines()
- Stdout linesCapturedOutput.getErrLines()
- Stderr linesCapturedOutput.getAllLines()
- All lines (stdout first, then stderr)
- Raw byte access:
CapturedOutput.getOutAsBytes()
- Raw stdout bytesCapturedOutput.getErrAsBytes()
- Raw stderr bytes
- Search methods:
CapturedOutput.contains(String)
- Search in both streamsCapturedOutput.outContains(String)
- Search in stdout onlyCapturedOutput.errContains(String)
- Search in stderr only
Chronological vs. Stream-Grouped Output:
The annotation provides two different ways to access captured output:
- Stream-grouped: where all stdout content is grouped together, followed by all stderr content.
- Chronological: where the exact order in which output occurred is preserved, interleaving stdout and stderr as it would appear in a real console. Each output event includes timestamp and stream type information.
Performance Considerations:
- Chronological tracking adds minimal overhead per output operation
- Each output operation (print, println, printf) creates one chronological entry
- Timestamps are captured using
Instant.now()
- Memory usage scales linearly with the number of output operations
Limitations:
- Does not capture output from native code or processes spawned by the JVM
- Console input (System.in) is not captured, only output streams
- Direct writes to file descriptors bypass the capture mechanism
- Chronological tracking granularity is per print/write operation, not per character
- Since:
- 1.0
- Author:
- Erik C. Thauvin
- See Also: