IO Testing

Jakob Jenkov
Last update: 2014-05-24

Components that perform IO can be a little tricky to unit test. In this text I will give a few suggestions to how you can test such classes.

The reason IO components are a little tricky to test is, that they usually either read from an InputStream or write to an OutputStream, or similar components. In order to test such components you need to be able to control the data that is read from an InputStream, and be able to access the data written to an OutputStream.

Testing Input Components

First let's look at a component that reads data from an InputStream. The component parses the data read, and breaks it up into String tokens, whenever it meets a comma (,). It's not a very sophisticated parser, but it's good enough for this example.

public class MyIOUnit {

    protected List<String> tokens = new ArrayList<String>();

    public void read(InputStream input) throws IOException {
        StringBuilder builder = new StringBuilder();

        int data = input.read();
        while(data != -1){
            if(((char)data) != ','){
                builder.append((char) data);
            } else {
                this.tokens.add(builder.toString().trim());
                builder.delete(0, builder.length());
            }

            data = input.read();
        }
    }
}

Notice how the read() method takes an InputStream as parameters. Let's look at how to write a unit test for this class.

import org.junit.Test;
import static org.junit.Assert.*;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.IOException;

public class MyIOUnitTest {

    @Test
    public void testRead() throws IOException {
        MyIOUnit unit = new MyIOUnit();

        byte[] data = "123,456,789,123,456,789".getBytes();

        InputStream input = new ByteArrayInputStream(data);

        unit.read(input);

        assertEquals("123", unit.tokens.get(0));
        assertEquals("456", unit.tokens.get(1));
        assertEquals("789", unit.tokens.get(2));
        assertEquals("123", unit.tokens.get(3));
        assertEquals("456", unit.tokens.get(4));
        assertEquals("789", unit.tokens.get(5));
    }
}

Notice how a byte array is created from a String, and then inserted into a ByteArrayInputStream instance. The ByteArrayInputStream is then used as input into the MyIOUnitTest.read() method. This way it is possible control the input of the unit during unit test.

Testing Output Components

It is also possible to test components that write to an OutputStream using a similar approach. What you need to do is to use an OutputStream which collects the written data inside it. The ByteArrayOutputStream class does this.

Here is a unit (class) that writes to an OutputStream:

public class MyIOUnit {

    protected List<String> tokens = new ArrayList<String>();

    public void write(OutputStream output) throws IOException {
        for(int i=0; i<tokens.size(); i++){
            if(i>0){
                output.write(',');
            }
            output.write(tokens.get(i).getBytes());
       }
    }
}

Here is the unit test which uses a ByteArrayOutputStream to collect the data written to the OutputStream passed to the MyIOUnit.write() method:

import org.junit.Test;
import static org.junit.Assert.*;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.ByteArrayOutputStream;


public class MyIOUnitTest {

    @Test
    public void testWrite() throws IOException {
        MyIOUnit unit = new MyIOUnit();

        ByteArrayOutputStream output = new ByteArrayOutputStream();

        unit.tokens.add("one");
        unit.tokens.add("two");
        unit.tokens.add("three");

        unit.write(output);

        String string = new String(output.toByteArray());
        assertEquals("one,two,three", string);
    }
}

Notice how the output.toByteArray() method is called, and passed into a String. After that, the created String is compared to the expected string, "one,two,three".

Reader's and Writer's

If your input or output component uses a Reader or Writer instead of an InputStream or OutputStream, you can use the classes CharArrayReader and CharArrayWriter instead of ByteArrayInputStream and ByteArrayOutputStream.

You may also be able to use the StringReader and StringWriter classes in your unit tests.

Jakob Jenkov

Featured Videos

Java Generics

Java ForkJoinPool

P2P Networks Introduction



















Close TOC
All Tutorial Trails
All Trails
Table of contents (TOC) for this tutorial trail
Trail TOC
Table of contents (TOC) for this tutorial
Page TOC
Previous tutorial in this tutorial trail
Previous
Next tutorial in this tutorial trail
Next