Friday 24 August 2012

Data driven tests with JUnit

I recently discovered a simple way to create data driven tests using JUnit.  It's so simple that I am surprised I missed it before.

Here is a code snippet:
public class DataDrivenTestExample extends TestCase {

    private final String expected;
    private final String actual;
 
    // must be named suite() for the JUnit Runner to pick it up
    public static Test suite() {
        TestSuite suite = new TestSuite();
        suite.addTest(new DataDrivenTestExample("One", "answer", "answer"));
        suite.addTest(new DataDrivenTestExample("Two", "result", "fail?"));
        suite.addTest(new DataDrivenTestExample("Three", "run-all-tests!", "run-all-tests!"));
        return suite;
    }
 
    protected DataDrivenTestExample(String name, String expected, String actual) {
        super(name);
        this.expected = expected;
        this.actual = actual;
    }

    /**
     * override this; default impl tries to reflectively find methods matching {@link TestCase#getName()}
     */
    @Override
    protected void runTest() throws Throwable {
        assertEquals(expected, actual);
    }
} 

 The first time I used JUnit for data driven test,  my code would iterate through a directory of files within a single test method.  Of course this does not provide very good defect localization when a test fails...

Then I thought, why not use parameterized test?
This almost works except that JUnit does not let you specify the name of the test!  So when you run it in your IDE or when you review a test report from your CI server you get:
  • testMethod[0]
  • testMethod [1]
 and so on as test names...

An alternative is to use a  JUnit add-on that provides named parameterized tests (it does a toString() of the parameters).  This is pretty good but it requires an extra third party library.

I just finished the first few hundred pages of Xunit Test Patterns and I feel like I finally understand JUnit (beyond the annotations!). There is even a Data Driven Test pattern which explains how it could be implemented by creating a test suite based on interpreting the data as test cases.  The above example does just that!

It would be fairly straightforward to write a FileDrivenTestCase that builds a test suite based on the files contained in a directory.  Then all you have to do is drop new files (inputs) with expected outputs.  It's also handy that setUp() and tearDown() still get called when you override runTest().

Happy data driven testing!