Spring and DBUnit Integration

Posted on October 24, 2004 by Scott Leberknight

On a new project I'm working on, we are using Hibernate as the persistence framework and Spring for DAOs using its HibernateDaoSupport class, declarative transaction management, and integration with Struts, among other things. We wanted to effectively unit test the persistent classes without writing the boilerplate code in our tests to insert and clean up test data. In the past I've written my own DBUnit-like framework, nowhere near as extensive, but it did the job. This time I decided to try DBUnit.

It took all of about 15 minutes to download, install, and get DBUnit integrated into our development environment. In DBUnit you subclass DatabaseTestCase, override two methods, getConnection() and getDataSet(), and provide test data in XML files. I didn't want all our developers to have to actually implement these methods every time they write a database-driven test case, so I extended DatabaseTestCase with the help of Spring to create default implementations of these methods in a BaseDatabaseTestCase. Spring is used to mainly to configure a DataSource from which a connection is obtained, in a dbunitContext.xml file which resides in the same package as BaseDatabaseTestCase. So the getConnection() method simply obtains the DataSource from the Spring application context defined in the XML file.

This solves the connection problem for all tests, but what about the dataset needed by DBUnit to prime the database prior to test execution? The dbunitContext.xml permits you to define a standard set of files containing DBUnit datasets that all extending tests will get by default. This is optional and should not be used to have the entire database populated before each test. You could use it for populating some tables that are required to always be populated in your application, such as reference tables or user tables or something similar.

The BaseDatabaseTestCase implements getDataSet() to combine the DataSet found in the Spring application context with a default DataSet for the specific test. This default DataSet is defined in an XML file that resides in the same package as the test and is named the same as the test with the file extension .dbunit.xml. For example, suppose you have a test MyTest.java which extends BaseDatabaseTestCase. Then to define the default DataSet for this test, simply create a file named MyTest.dbunit.xml in the same package as MyTest. BaseDatabaseTestCase automatically looks for this file and loads the DataSet contained within it. If there are any standard DataSets defined in dbunitContext.xml, they are combined with the default DataSet to prime the database for the test.

This allows developers who need to create unit tests for database-driven code to simply extend BaseDatabaseTestCase, write an XML file containing the test data, and implement standard JUnit testXXX() methods. Pretty nifty.

Mythical Man Month Lives On...

Posted on October 24, 2004 by Scott Leberknight

I have a friend who works on a project with a manager who typifies the problems with the software development industry. We'll call this developer Bob and the manager Jim. Over the past couple of months Bob has mentioned some very disturbing things that Jim has said and done in running, if one could call it that, the project. For example, Jim started off by not allowing Bob, the lead developer, to use a persistence framework and instead mandated he use straight JDBC because of the "risks" of the frameworks. This is of course nonsense since the real risk is not using a tried and true persistence framework and rolling your own.

Another brilliant decree made by Jim was that developers cannot spend time developing unit tests because they will slow the development effort and are not really needed because the developers "are very careful to write bug-free code". Excuse me? Did I just actually hear that? Yes in fact I did. As a developer I am careful to not introduce bugs into my designs and code, but I in no way do I assume I write bug-free code. Unit tests of course are one way to ensure high-quality code and in fact speed development, since many bugs are caught and fixed much earlier than they otherwise would be. In addition, a quality suite of unit tests that can be run at the touch of a button are wonderful for regression testing new changes. Even worse, Jim told the customer that the developers were very careful to write bug-free code and that they would only need one week to fix any and all bugs that were found by the integration testers and the customers! Bob was not at all pleased to hear this of course.

Another fun thing Jim has done on several occasions is to inform the customer that the developers intended to be done early, so some additional new requirements could easily be added. In reality, Bob told me many times there was no way they were even going to meet their original deadline much less with loads of new requirements. Finally after recognizing they were not going to make the initial (unrealistic) deadline, Jim got the customer to extend the deadline several months. Then Jim did something that Fred Brooks would, I am sure, not advocate. Jim told Bob that since they had a few more months, this would be the perfect time to bring on five more developers to "speed up development". So much for Brooks truism that adding developers to a late project makes it later, whcich has been proven out over several decades.

In the end, Bob has started looking around for other projects and probably not be with that project much longer.