Unit testing Hibernate DAOs with dbUnit

Posted by Chris Tue, 08 Apr 2008 16:51:00 GMT

For the longest time our unit test suite had lots of DAO unit tests that, in some cases, were using dbUnit already, but were connecting to an actual Oracle database while running. They were also automatically loading up the Spring DAO context (all of the DAOs) and all of the Hibernate mapping files for the whole project. This resulted in test cases that took roughly 20 seconds to run, which was way to long for a unit test to run.

In order to try to make them run faster, and be more self contained, we have modified the tests so that they no longer rely on the Spring context and loading all the hibernate mapping files. We have also moved to use HSQLDB, which HIbernate supports, an in-memory SQL database implemented in Java. Initial tests are positive, with many of the tests that previously took 15-20 seconds taking 3-4 seconds to run.

In order to do this, I had to switch from using a Spring-configured hibernate environment to a hand-configured hibernate environment that allows the test classes to define the hibernate mapping files to use. The important steps in doing this were to create a Hibernate Configuration object and define all of the HSQLDB/Hibernate related configuration for the tests and set up some automatic loading of test data via dbUnit.

Hibernate Configuration was pretty simple, and looks like this in my parent class setUp() method:

Configuration config = new Configuration().
setProperty("hibernate.dialect","org.hibernate.dialect.HSQLDialect").
setProperty("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver").
setProperty("hibernate.connection.url","jdbc:hsqldb:mem:nwawebres").
setProperty("hibernate.connection.username", "sa").
setProperty("hibernate.connection.password", "").
setProperty("hibernate.connection.pool_size", "1").
setProperty("hibernate.connection.autocommit", "true").
setProperty("hibernate.current_session_context_class", "thread").
setProperty("hibernate.show_sql", "true").
setProperty("hibernate.cglib.use_reflection_optimizer", "false").
setProperty("hibernate.hbm2ddl.auto", "create-drop");

This sets up Hibernate to use an in-memory HSQLDB that is re-initialized for every test run and has tables automatically created based on the specified Hibernate mapping files. In order to specify the hibernate mapping files, I created an abstract method that returns a list of strings, then I do the following:

Iterator i = getHibernateMappingFiles().iterator();
while(i.hasNext()) {
    String file = (String) i.next();
    System.out.println("Adding mapping file: " + file);
    InputStream input = getClass().getResourceAsStream(file);
    if(input == null) {
        input = getClass().getClassLoader().getResourceAsStream(file);
    }
    config.addInputStream(input);
}

After the hibernate mapping files are specified. I then have code that initialized the new database with base data based on the test class (See below for explanation of getDatabaseConnection() and getDatasetXmlLocation()):

_sessionFactory = config.buildSessionFactory();
_hibernateTemplate = new HibernateTemplate(_sessionFactory);
_hibernateTemplate.afterPropertiesSet();

Session session = _sessionFactory.openSession();
session.beginTransaction();
DatabaseConnection dbconn = getDatabaseConnection(session.connection());
try {
    String xml = getDatasetXmlLocation();
    InputStream input = getClass().getResourceAsStream(xml);
    IDataSet dataSet = new XmlDataSet(input);
    DatabaseOperation.INSERT.execute(dbconn, dataSet);
}
catch (DataSetException ex) {
    System.out.println("Data not loaded " + ex.getClass().getName() + ", due to DataSetException");
    System.out.println("Message: " + ex.getMessage());
    // catch it. If there is no dataset avaible, exit graciously!
} catch (DatabaseUnitException e) {
    System.out.println("Data not loaded" + e.getMessage() + ", due to Database Unit Exception");
    // catch it. If there is no dataset avaible, exit graciously!
}
finally {
    session.close();
}

This method uses two other methods: getDatabaseConnection() and getDatasetXmlLocation():

protected String getDatasetXmlLocation() {
    int idx = getClass().getName().lastIndexOf(".");
    return getClass().getName().substring(idx + 1) + ".xml";
}

And:

protected DatabaseConnection getDatabaseConnection(
        Connection connection) {
    DatabaseConnection dbconn =  new DatabaseConnection(connection);
    DatabaseConfig config = dbconn.getConfig();
    config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new HsqldbDataTypeFactory());
    return dbconn;
}

This method can be used by child classes to get a database connection for verifying data in the database via dbUnit, like so:

QueryDataSet ds = new QueryDataSet(conn);
ds.addTable("CATALOG", "SELECT * FROM CATALOG WHERE id = " + catagoryId);
ITable table = ds.getTable("CATALOG");
...
Number id = (Number) table.getValue(0, "ID");
assertEquals("Wrong id", id.longValue(), cat.getId());

After most of this goes in a parent class, child test classes just need to implement get getHibernateMappingFiles() method and define a setUp() method that creates the test DAO class and injects the _hibernateTemplate member variable from the parent class into the DAO.

Comments

Leave a comment

Comments