07 December 2011

Lots of classes and applications I've written at some point output information to System.out and System.err. Sometimes it's quick-and-dirty debugging but if the output is meaningful, testing what's written to either is a pretty simple thing to do.

For this post, I've created a helper class with two static methods that write to System.out and System.err:

class SystemOutAndSystemErrHelper {
    public static void writeToSystemOut(String text) {
        System.out.println("writing '" + text + "' to stdout");
    }
    
    public static void writeToSystemErr(String text) {
        System.err.println("writing '" + text + "' to stderr");
    }
    
}

Where System.out and System.err write output to can be overridden by calling System.setOut and System.setErr, supplying PrintStream instances. The PrintStream objects themselves are instantiated with an OutputStream instance that I can easily interrogate later.

In this first test, I instantiate a PrintStream with a ByteArrayOutputStream:

@Test
public void dataShouldBeWrittenToSystemOut() {
    String newline = System.getProperty("line.separator");
    
    // the PrintStream will be instantiated with a ByteArrayOutputStream
    //  that we'll read from later
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    PrintStream ps = new PrintStream(baos);
    
    System.setOut(ps);
    
    SystemOutAndSystemErrHelper.writeToSystemOut("some output text");
    
    String actual = new String(baos.toByteArray());
    String expected = "writing 'some output text' to stdout" + newline;
    
    assertThat(actual, is(expected));
    
}

After calling System.setOut with the constructed PrintStream, the helper object writes some text to System.out. Testing is as easy then as constructing a String with the bytes from the ByteArrayOutputStream and making an assertion about the contents of the output.

Testing the output to System.err is very similar, the only difference being calling System.setErr in my test instead:

@Test
public void dataShouldBeWrittenToSystemErr() {
    String newline = System.getProperty("line.separator");
    
    // the PrintStream will be instantiated with a ByteArrayOutputStream
    //  that we'll read from later
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    PrintStream ps = new PrintStream(baos);
    
    System.setErr(ps);
    
    SystemOutAndSystemErrHelper.writeToSystemErr("some error text");
    
    String actual = new String(baos.toByteArray());
    String expected = "writing 'some error text' to stderr" + newline;
    
    assertThat(actual, is(expected));
    
}

If I'm not concerned about which handle the data was written to and only that it got written somewhere, I can pass the same PrintStream to System.setErr and System.setOut:

@Test
public void dataShouldBeWrittenToSystemOutAndSystemErr() {
    String newline = System.getProperty("line.separator");
    
    // the PrintStream will be instantiated with a ByteArrayOutputStream
    //  that we'll read from later
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    PrintStream ps = new PrintStream(baos);
    
    System.setOut(ps);
    System.setErr(ps);
    
    SystemOutAndSystemErrHelper.writeToSystemOut("some output text");
    SystemOutAndSystemErrHelper.writeToSystemErr("some error text");
    
    String actual = new String(baos.toByteArray());
    String expected = "writing 'some output text' to stdout" + newline +
                    "writing 'some error text' to stderr" + newline;
    
    assertThat(actual, is(expected));
    
}

In my examples I used a ByteArrayOutputStream for simple conversion to a String, but I could've just as easily used a FileOutputStream, ObjectOutputStream, or any other OutputStream subclass.