Friday, March 23, 2007

Writing JUnit 4 parameterized tests with varargs data

One of the many excellent new features of JUnit 4 is parameterized tests. These are data-driven tests where a test case runs repeatedly against a collection of test data, as if each run were its own test case.

A standard example:

@RunWith(Parameterized.class)
public class ExampleDataDrivenTest {
    public static final class Data {
        private final String name;

        public Data(final String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return name;
        }
    }

    private final List<Data> data;

    public ExampleDataDrivenTest(final Data datum1, final Data datum2) {
        System.out.println("ExampleDataDrivenTest");

        this.data = asList(datum1, datum2);
    }

    @Test
    public void dumpData() {
        for (final Data datum : data)
            System.out.println("datum = " + datum);
    }

    @Parameters
    public static Collection<Data[]> data() {
        final Data[][] data = new Data[][]{
                {new Data("apple"), new Data("core")}};

        return asList(data);
    }
}

This produces:

ExampleDataDrivenTest
datum = apple
datum = core

This works great for a fixed number of test data, but I want to test a variable number of data. The change is straight-forward once you recall that the varargs Java language feature is implemented as an array:

@RunWith(Parameterized.class)
public class ExampleDataDrivenTest {
    public static final class Data {
        private final String name;

        public Data(final String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return name;
        }
    }

    private final List<Data> data;

    public ExampleDataDrivenTest(final Data... data) {
        System.out.println("ExampleDataDrivenTest");

        this.data = asList(data);
    }

    @Test
    public void dumpData() {
        for (final Data datum : data)
            System.out.println("datum = " + datum);
    }

    @Parameters
    public static Collection<Data[][]> data() {
        final Data[][][] data = new Data[][][]{
                {{new Data("apple"), new Data("core")}}};

        return asList(data);
    }
}

Reflect on the constructor and you see:

public ExampleDataDrivenTest(ExampleDataDrivenTest$Data[])

This makes the change more obvious.

I can now write my annotated parameters as:

    @Parameters
    public static Collection<Data[][]> data() {
        final Data[][][] data = new Data[][][]{{{ /* no data */ }},
                {{new Data("apple"), new Data("core")}},
                {{new Data("baltimore")}}};

        return asList(data);
    }

To produce:

ExampleDataDrivenTest
ExampleDataDrivenTest
datum = apple
datum = core
ExampleDataDrivenTest
datum = baltimore

Saturday, March 10, 2007

Storing jars in a Maven project

One of the sweetest things about Maven is that depedencies are stored external to your sources: if you depend on Apache Commons Lang 2.3, for example, then you can stick this in the dependencies section of your pom.xml and stop worrying:

<depedency>
    <groupId>commons-lang</groupId>
    <artifactId>commons-lang</artifactId>
    <version>2.3</version>
</dependency>

But what do you do when your dependency is not available in a public Maven repository? For example, the library is too new to be widely available, or is proprietary like the Oracle JDBC driver?

One tack is to establish an internal repository and refer to that in your pom.xml. But sometimes that is not practical, perhaps because of corporate network use restrictions, or for very small projects.

Before Maven, common practice was to stash dependencies into a directory named lib/ or similar underneath the root of your project sources. When no other solution presents itself, you can follow that practice but keep using Maven.

In your pom.xml, add this top-level section:

<repositories>
    <repository>
        <id>project</id>
        <name>Project Maven Repository</name>
        <layout>default</layout>
        <url>file:///./lib</url>
    </repository>
</repositories>

This establishes the lib/ directory underneath your project as a Maven repository relative to your project root. Now you can stash dependencies there, store them with the rest of your project resources, but keep using Maven.

A little bit more

Often I have to re-lookup the documentation on deploy:deploy-file so that I can put a dependency in an internal repository or in the project repository as described above. Here is a magic incantation example:

$ cd /PATH-TO-guice-1.0-DOWNLOAD
# Checkout the SVN 1.0 tag sources to src/
# Note -- need to use the -Dversion=1.0 switch
# because pom says 1.0-RC2 in spite of tag.  Then:
$ mvn deploy:deploy-file \
    -Durl=file://C:/FULL-PROJECT-PATH/lib \
    -DrepositoryId=project -Dfile=guice-1.0.jar \
    -DpomFile=src/pom.xml -Dversion=1.0
# Pack up the javadocs and sources into jars, then:
$ mvn deploy:deploy-file \
    -Durl=file://C:/FULL-PROJECT-PATH/lib \
    -DrepositoryId=project \
    -Dfile=guice-1.0-javadocs.jar \
    -DpomFile=src/pom.xml -Dversion=1.0 \
    -Dclassifier=javadocs
$ mvn deploy:deploy-file \
    -Durl=file://C:/FULL-PROJECT-PATH/lib \
    -DrepositoryId=project \
    -Dfile=guice-1.0-sources.jar \
    -DpomFile=src/pom.xml -Dversion=1.0 \
    -Dclassifier=sources

I now have the splediferous Guice 1.0 dependency in my project repository, and can use it in my pom.xml with:

<depedency>
    <groupId>com.google.inject</groupId>
    <artifactId>guice</artifactId>
    <version>1.0</version>
</dependency>