SystemOutputRule.java

package uk.org.lidalia.test;

import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;

import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;

/**
 * A <a href="https://github.com/junit-team/junit/wiki">JUnit</a> rule that facilitates testing code that prints to
 * {@link System#out} or {@link System#err}.
 * <p>
 * At the start of each test the System.out and System.err PrintStreams are replaced with ones that buffer data passed to them in
 * memory, allowing the test to retrieve the contents of the buffer at any time and assert on it. At the end of the test the
 * original PrintStreams are restored allowing access to the console again.
 */
public class SystemOutputRule implements TestRule {

    private final ByteArrayOutputStream sysOut = new ByteArrayOutputStream();
    private final ByteArrayOutputStream sysErr = new ByteArrayOutputStream();
    private final PrintStream originalSysOut = System.out;
    private final PrintStream originalSysErr = System.err;

    @Override
    public Statement apply(final Statement base, final Description description) {
        return new DebriefableSystemOutputsStatement(base, sysOut, sysErr, originalSysOut, originalSysErr);
    }

    /**
     * @return the data that has been written to System.out since the test began
     */
    public String getSystemOut() {
        return sysOut.toString();
    }

    /**
     * @return the data that has been written to System.err since the test began
     */
    public String getSystemErr() {
        return sysErr.toString();
    }

    private static class DebriefableSystemOutputsStatement extends Statement {
        private final Statement base;
        private final OutputStream sysOut;
        private final OutputStream sysErr;
        private final PrintStream originalSysOut;
        private final PrintStream originalSysErr;

        public DebriefableSystemOutputsStatement(final Statement base, final OutputStream sysOut, final OutputStream sysErr,
                                                 final PrintStream originalSysOut, final PrintStream originalSysErr) {
            super();
            this.base = base;
            this.sysOut = sysOut;
            this.sysErr = sysErr;
            this.originalSysOut = originalSysOut;
            this.originalSysErr = originalSysErr;
        }

        @Override
        public void evaluate() throws Throwable {
            try {
                System.setOut(new PrintStream(sysOut));
                System.setErr(new PrintStream(sysErr));
                base.evaluate();
            } finally {
                System.setOut(originalSysOut);
                System.setErr(originalSysErr);
            }
        }
    }
}