A month or two ago we introduced a new DAO testing framework to my current project. It still made use of DB Unit but had a different approach to that of AppFuse which the project took advantage of as base. Before, the DB Unit Ant task would insert data from an XML file and would run all the tests. We had three problems with this:
- Data is only inserted once, at the start of the test which is used by one or more tests. We need to be extra careful that the database state is maintained. Typical situation is when testing a save method - either remove the additional rows that were inserted or combine the add-save-remove tests into one. I do understand though that loading data can take a long time, which I experienced in my last major project.
- Does not really work well when running inside an IDE like Eclipse. When a test fails there is an additional reason that we consider to explain the failure, and that is the database state is not the expected one before the start of the test. Thus a test is not really repeatable in the strictest sense.
- Development pretty much involves writing migration scripts to the current version. Our strategy for this to write the migration script when we introduce a change to the database. This avoids having to write all the migration scripts near release (continuous integration?) but easily prevents us from having Hibernate generate the schema nor have the DAO test (over)write the data in the database.
The problems above pretty much resulted in a list of requirements when we started working on the new test framework:
- Should maintain database state at the start of each testing method.
- Should not be slow (possible side effect of #1).
- Should be executable inside an IDE.
- Should execute tests in a separate database to not disturb the (large) development data.
To address the first two requirements we had the test super class load only a subset of the data, that which the test case being executed needed, before each test was executed. We forced a method getDataSetLocations() that returned a String array of XML file paths to load. We are then free to either specify data on per table or per test case basis, and to mix and match test data to avoid duplication. Below is a straightforward implementation, with the XML paths stored inside a constant/interface:
public abstract class BaseDaoTestCase extends AbstractTransactionalDataSourceSpringContextTests {
@Override
protected String[] getConfigLocations() {
// Specify Spring XML files
}
protected abstract String[] getDataSetLocations();
@Override
protected void onSetUpInTransaction() throws Exception {
super.onSetUpBeforeTransaction();
Connection conn = DriverManager.getConnection(...);
IDatabaseConnection connection = new DatabaseConnection(conn);
try {
for (String dataSetLocation : getDataSetLocations()) {
IDataSet dataSet = new XmlDataSet(
new FileReader(new File(dataSetLocation)));
DatabaseOperation.CLEAN_INSERT.execute(connection, dataSet);
}
} finally {
connection.close();
}
}
}
public class FooTest extends BaseDaoTestCase {
private FooDao fooDao;
public void setFooDao(FooDao fooDao) {
this.fooDao = fooDao; // autowired by Spring
}
@Override
protected String[] getDataSetLocations() {
return new String[] {
DataSetLocations.TBL_USER, // data from tbl_user table
DataSetLocations.FOO_DAO // specific to FooDaoTest
}
}
public void testFoo() {
...
}
}
We hit a bit of a snag when implementing the third requirement, as the test could not find the Spring context files as they were moved for the directory they reside in to a location visible in the classpath. Ended up adding the said directories into Eclipse's classpath while adding exceptions to other file types other than XML.
The last requirement was satisfied by switching to the test database during test time. This is a similar approach to what Rails uses for it's separate production, development, and testing database setup. hbm2ddl was set to generate the test database schema during the `setup` Ant task to ensure that it is always present. We then modified our build file to do the necessary switching and made sure that Eclipse loaded the context file that pointed to the test database first.


