DBUnit 2.2, Spring, and Testing

Christian Nelson ·

You may have noticed that DBUnit changed its connection closing behavior in v2.2. We noticed it when tests deriving from our custom DatabaseTestCase implementation (which uses DBUnit) starting failing. Initially we just reverted back to version 2.1. Since then I’ve discovered what the problem was and found a workaround, and while I was at it, I created some nice utilities to be used in tests which need data fixtures.

Problem

In v2.2 DBUnit introduces some new abstractions, one of which is the IDatabaseTester. Whether it’s by design or by accident, the default implementation closes connections after executing its operations (more info). The end result is that in our unit tests, the database connection is closed before the test can run.

Solution

I’ve created two simple util classes for loading test data into a database without closing the connection:

/** A helper for loading data sets into unit tests. */
public class DatabaseUtils {
    public static void loadDataSet(Class clazz, final DataSource dataSource) throws Exception {
        IDataSet dataSet = new FlatXmlDataSet(TestUtils.datasetInputStream(clazz));
        IDatabaseTester tester = new ExistingConnectionDatabaseTester(dataSource);
        tester.setDataSet(dataSet);
        tester.onSetup();
    }
}
/** A special DatabaseTester that doesn’t close the connection when its done. */
public class ExistingConnectionDatabaseTester extends AbstractDatabaseTester {
    private DataSource dataSource;

    public ExistingConnectionDatabaseTester(DataSource dataSource) {
        super();
        this.dataSource = dataSource;
    }

    public IDatabaseConnection getConnection() throws Exception {
        return new DatabaseConnection(DataSourceUtils.getConnection(dataSource));
    }

    public void closeConnection(IDatabaseConnection connection) throws Exception {
        // Don't close that connection!
    }
}

The first of these depends on another test-related utility, TestUtils.java (see below), which converts a class to a path. The second depends on Spring’s DataSourceUtils.

/**
 * A set of utilities that generate paths from classnames.  This is useful when
 * making reference to resources used by test cases, when the resources are
 * located in a directory path matching the class' package.
 */
public class TestUtils {

    public static final String TEST_PREFIX = "src/test/resources/"; // Maven2 default

    public static String pathString(Class clazz) {
        return pathString(TEST_PREFIX, clazz, "");
    }

    public static String pathString(Class clazz, String resource) {
        return pathString(TEST_PREFIX, clazz, resource);
    }

    public static String pathString(String prefix, Class clazz, String resource) {
        prefix = (prefix != null ? prefix : "");

        StringBuffer sb = new StringBuffer();
        sb.append(prefix);

        if (!prefix.endsWith("/")) {
            sb.append("/");
        }

        sb.append(ClassUtils.classPackageAsResourcePath(clazz));
        sb.append("/");
        sb.append(resource);

        return sb.toString();
    }

    public static InputStream pathInputStream(Class clazz, String resource)
        throws FileNotFoundException {
        return pathInputStream(TEST_PREFIX, clazz, resource);
    }

    public static InputStream pathInputStream(String prefix, Class clazz, String resource)
        throws FileNotFoundException {
        return new FileInputStream(pathString(prefix, clazz, resource));
    }

    public static InputStream datasetInputStream(Class clazz) throws FileNotFoundException {
        return pathInputStream(TEST_PREFIX, clazz, ClassUtils.getShortName(clazz) + ".xml");
    }
}

These three classes give us everything we need to load data into our test database using DBUnit. And TestUtils can be used to load other test resources from the classpath as well, like images, documents, CSVs, etc.

Here’s an example of how it can be used in a DAO test based on Spring’s test classes.

public class ArtistHibernateDaoTest extends AbstractAnnotationAwareTransactionalTests {

    private ArtistHibernateDao artistDao;

    public void setArtistDao(ArtistHibernateDao artistDao) {
        this.artistDao = artistDao;
    }

    protected String[] getConfigLocations() {
        return new String[]{"applicationContext-database.xml", "applicationContext-hibernate.xml"};
    }

    protected void onSetUpInTransaction() throws Exception {
        DatabaseUtils.loadDataSet(getClass(), getJdbcTemplate().getDataSource());
    }

    public void testFindAllNames() {
        List names = artistDao.findAllNames();
        assertEquals(3, names.size());
        assertTrue(names.contains("The Cure"));
        assertTrue(names.contains("Depeche Mode"));
        assertTrue(names.contains("New Order"));
    }
}

onSetUpInTransaction() loads the data set using the convention of (packagename).(ClassName).xml before each test (e.g. com.c5.PizzaTest loads the file com/c5/PizzaText.xml on the classpath). In this case the data is loaded in the same transaction as the test so that no clean-up is necessary when the test is completed. These tests run fast!

We’ve been talking about moving away from our custom DatabaseTestCase class hierarchy. The above functionality in conjunction with Spring’s test hierarchy could be a good start.

On a somewhat related note, there is a new unit testing framework called Unitils which does this sort of thing and much more. I haven’t used it but it sounds interesting.

Christian

Christian Nelson
Christian Nelson

Christian is a software developer, technical lead and agile coach. He's passionate about helping teams find creative ways to make work fun and productive. He's a partner at Carbon Five and serves as the Director of Engineering in the San Francisco office. When not slinging code or playing agile games, you can find him trekking in the Sierras and playing with his daughters.

What can we help you with?

Tell us a bit about your project, or just shoot us an email.

Interested in a Career at Carbon Five? Check out our job openings.