How do you mock an output stream?

Tom Anderson picture Tom Anderson · Jun 18, 2011 · Viewed 20.1k times · Source

By 'output steam' i mean any object which receives a sequence of bytes, or characters or whatever. So, java.io.OutputStream, but also java.io.Writer, javax.xml.stream.XMLStreamWriter's writeCharacters method, and so on.

I'm writing mock-based tests for a class whose main function is to write a stream of data to one of these (the XMLStreamWriter, as it happens).

The problem is that the stream of data is written in a series of calls to the write method, but what matters is not the calls, but the data. For example, given an XMLStreamWriter out, these:

out.writeCharacters("Hello, ");
out.writeCharacters("world!");

Are equivalent to this:

out.writeCharacters("Hello, world!");

It really doesn't matter (for my purposes) which happens. There will be some particular sequence of calls, but i don't care what it is, so i don't want to write expectations for that particular sequence. I just want to expect a certain stream of data to be written any which way.

One option would be to switch to state-based testing. I could accumulate the data in a buffer, and make assertions about it. But because i'm writing XML, that would mean making some fairly complex and ugly assertions. Mocking seems a much better way of dealing with the larger problem of writing XML.

So how do i do this with a mock?

I'm using Moxie for mocking, but i'm interested in hearing about approaches with any mocking library.

Answer

Mr.Eddart picture Mr.Eddart · Jun 27, 2011

A fairly elegant strategy to test output or input streams is to use PipedInputStream and PipedOutputStream classes. You can wire them together in the set up of the test, and then check what has been written after the target method is executed.

You can work the other direction preparing some input and then let the test read this prepared data from the input stream as well.

In your case, you could just mock that "out" variable with a PipedOutputStream, and plug a PipedInputStream to it this way:

private BufferedReader reader;

@Before
public void init() throws IOException {
    PipedInputStream pipeInput = new PipedInputStream();
    reader = new BufferedReader(
            new InputStreamReader(pipeInput));
    BufferedOutputStream out = new BufferedOutputStream(
            new PipedOutputStream(pipeInput))));
    //Here you will have to mock the output somehow inside your 
    //target object.
    targetObject.setOutputStream (out);
    }


@Test
public test() {
    //Invoke the target method
    targetObject.targetMethod();

    //Check that the correct data has been written correctly in 
    //the output stream reading it from the plugged input stream
    Assert.assertEquals("something you expects", reader.readLine());
    }