Annotation 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:

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: