32

I'm writing integration tests using JUnit to automate the testing of a console based application. The application is homework but this part isn't the homework. I want to automate these tests to be more productive -- I don't want to have to go back and retest already tested parts of the application. (Standard reasons to use Unit tests)

Anyway, I can't figure out or find an article on capturing the output so that I can do assertEquals on it nor providing automated input. I don't care if the output/input goes to the console/output pane. I only need to have the test execute and verify the the output is what is expected given the input.

Anyone have an article or code to help out with this.

skaffman
  • 381,978
  • 94
  • 789
  • 754
Frank V
  • 23,732
  • 32
  • 98
  • 142

3 Answers3

54

Use System.setOut() (and System.setErr()) to redirect the output to an arbitrary printstream - which can be one that you read from programmatically.

For example:

final ByteArrayOutputStream myOut = new ByteArrayOutputStream();
System.setOut(new PrintStream(myOut));

// test stuff here...

final String standardOutput = myOut.toString();
Andrzej Doyle
  • 97,637
  • 30
  • 185
  • 225
  • 1
    So simply going `PrintStream _out = System.out;` Won't work? – Frank V Jan 30 '10 at 20:37
  • It would - i.e. you'd have a reference to the existing output stream - but you can't *read* anything from it as there are no appropriate methods to do so on the general `PrintStream` interface. The technique involves setting the output to a specific printstream you know how to read from. – Andrzej Doyle Jan 30 '10 at 20:38
7

The System class has methods setIn(), setOut() and setErr() that allow you to set the standard input, output and error streams, e.g. to a ByteArrayOutputStream that you can inspect at will.

Michael Borgwardt
  • 327,225
  • 74
  • 458
  • 699
3

Here is the solution in place of ByteArrayOutputStream. It does not add anything to the idea of System.setOut. Rather, I want to share the implementation that is better than capturing everything into ByteArrayOutputStream. I prefer to capture only selected information and let all log messages to appear in the console as they are logged rather than capturing everything into a balckbox (of which size?) for later processing.

/**
 * Once started, std output is redirected to this thread. 
 * Thread redirects all data to the former system.out and
 * captures some strings.*/
static abstract class OutputCaputre extends Thread {

    // overrdie these methods for System.err
    PrintStream getDownstream() { return System.out;}
    void restoreDownstream() { System.setOut(downstream);}

    // will be called for every line in the log
    protected abstract void userFilter(String line);

    final PrintStream downstream;
    public final PipedInputStream pis;
    private final PipedOutputStream pos;
    OutputCaputre() throws IOException {
        downstream = getDownstream();

        pos = new PipedOutputStream();
        pis = new PipedInputStream(pos);
        System.setOut(new PrintStream(pos));

        start();
    }

    public void run() {
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(pis));

            // once output is resotred, we must terminate
            while (true) {
                String line = br.readLine();
                if (line == null) {
                    return;
                }
                downstream.println(line);
                userFilter(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void terminate() throws InterruptedException, IOException {
        restoreDownstream(); // switch back to std
        pos.close(); // there will be no more data - signal that
        join(); // and wait until capture completes
    }
};

Here is an example of using the class:

OutputCaputre outputCapture = new OutputCaputre() {
    protected void userFilter(String line) {
        downstream.println("Capture: " + line);
    }       
};
System.out.println("do you see me captured?");
// here is your test    
outputCapture.terminate(); // finally, stop capturing
Val
  • 1
  • 8
  • 38
  • 59