I’m currently working on a framework using a test driven approach. JUnit is extensively used to ensure quality. Data Access Objects (DAO) for an example project are tested too in order to test O/R mapping configurations. These DAO require plenty of reference data. In the past I had some fairly convenient helper classes that provide a useful test setup. I am mostly using Apache Derby (runtime + testing). Testing runs with an embedded Derby database.

Lately I have experienced that my test suite slows down (number of tests and amount of data that is imported for each test) with every test case added. I also realized that I need to extend my tests to cover other database systems as well. So I had to take action to deal with this test performance. I was in need of a tool that allows to organize and import my test data more efficiently and provide this test data for multiple database platforms. I knew that there is such a OpenSource product like DbUnit but never had time to look at it closer. After a first glimpse I realized that it was a perfect match for me. DbUnit helps with importing test data (it can do data comparison too!) by allowing to move test data to sources of different formats (I’m using the FlatXmlDataSet).

For each test case now I can easily create XML Files (backed by DTD) that contain relevant test data in a platform neutral way. Before I was using SQL commands for importing data and thus often required platform specific SQL syntax (especially for handling generated id values). Previously the setup (as a Spring bean) looked like

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<bean id="expenseDaoTestSetup" class="at.martinahrer.blueprint.dao.test.TestSetup">
  <property name="table">
      <value>AbstractExpense</value>
  </property>
  <property name="dataSet">
      <list>
          <value>insert into abstractexpense (comment) values ('TEST')</value>
          <value>
              insert into expense (id, amount, date) values (IDENTITY_VAL_LOCAL(), 0, TIMESTAMP('2006-01-01',
              '00.00.00'))
          </value>
      </list>
  </property>
  <property name="entity">
      <bean class="at.martinahrer.freelancer.model.Expense">
          <property name="comment" value="TEST" />
          <property name="amount">
              <value type="java.lang.Double">0.0</value>
          </property>
          <property name="date">
              <value type="java.util.Date">2006.01.01</value>
          </property>
      </bean>
  </property>
  <property name="example">
      <bean class="at.martinahrer.freelancer.model.Expense">
          <property name="comment" value="TEST" />
          <property name="amount">
              <value type="java.lang.Double">0.0</value>
          </property>
      </bean>
  </property>
</bean>

So DbUnit helps boosting test performance by helping organize test data much better. In addition I switched from Apache Derby embedded to HSQLDB in-memory since Derby gave me troubles with importing reference data with pre-definied id values for auto-generated id values. Besides HSQLDB seems to be a little faster.

Now my test setup only provides a list of XML file names for importing test data.

1
2
3
4
5
<property name="dataSet">
  <list>
      <value>ExpenseDataSet.xml</value>
  </list>
</property>

The XML is backed by a DTD schema and file looks like

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE dataset SYSTEM "DataSetSchema.dtd">
<dataset>
  <abstractexpense ID="1" COMMENT="TEST" />
  <expense ID="1" AMOUNT="0" DATE="2006-01-01 00:00:00.0"/>
</dataset>

For creating the DTD only a few lines of code are required.

1
2
3
4
5
6
7
8
9
10
IDatabaseConnection connection = new DatabaseConnection(getSessionFactory().getCurrentSession().connection());

// write DTD file
Writer writer = new StringWriter();
try {
  FlatDtdDataSet.write(connection.createDataSet(), writer);
} catch (Exception e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
}

Also importing XML based test data is as simple

1
2
3
4
5
6
IDatabaseConnection dbConnection = new DatabaseConnection(connection);
dbConnection.getConfig().setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new HsqlDataTypeFactory());
for (Object dataSet : dataSets) {
  DatabaseOperation.INSERT.execute(dbConnection, new FlatXmlDataSet(getClass().getClassLoader()
          .getResourceAsStream(dataSet.toString())));
}

The current version of DbUnit has a problem with HSQLDBs new BOOLEAN type, so it requires a data type factory as I learned from Carbon Five.

Comments