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.

Unit Testing only public methods is bad 1

Posted by Chris Thu, 27 Mar 2008 19:51:00 GMT

I think I’ve had the argument about testing only the public interface for a class multiple times now, probably at multiple jobs, and I’ve never really gotten a good answer about it. My point of view is that only testing the public interface is the wrong way to go about unit testing.

The way I see it, unit testing should test “units”, and in the case of software that translates in my head to “units of work”. Public interface methods are not “unit of work” from the point of view of the person writing or maintaining the software (the person writing the unit tests). Public interface methods may the “units of work” for a QA tester or an integration tester, but they are not for the author of the code.

From experience, taking the approach of testing the public interface for a class often leads to very difficult to maintain unit tests. Very often, public interface methods will utilize several logic branches and several “helper” methods that are often private or protected. They can often deal with complex and deeply nested datastructures. These factors can lead to unit tests can become very difficult to set up, give a false sense of coverage because they don’t exercise all logical paths, or are hard to maintain because the setup is so unwieldy.

The alternative is breaking your code up into the most atomic units that make sense and then testing these individually. This means testing all of the private and protected methods and not just the public interface. The difficulty in this arises when trying to determine how to write the tests for your public methods. The public methods still accomplish the same data processing regardless of how you refactor your code or write your tests for the “helper” methods, and testing the smaller methods doesn’t remove logical complexity from the system as a whole.

At this point in time, the only thing I can think of is the suggestion to make sure that no single method is responsible for too much decision making. Maybe the best practice is to attempt to have a system where a single method tries to only use a single layer of a datastructure to make it’s decision, and leaves other decisions to other methods. This way you can construct a test for a method by only providing a single layer of the datastructure, which should hopefully limit the amount to setup you need to do. I’m still working on this.