Lessons on testing a JPA Dao

I wanted to explore unit testing JPA DAO and models. Hand crafting solutions is quite time consuming. I found something called Unitils which refines another project Dbunit. In theory it should significantly reduce the complexity and save some time. So one Saturday, I sat down to explore the space and write this blog.


One way to test a DAO method is to provide a data fixture written in java. This will provide a populated in memory database and immutable objects that contain the data inserted.  The immutable objects can then be used later for making assertions against the data returned from the DAO method under test.  This describes a similar pattern: Unit Test with in memory Db.  I wrote a more modern version using the EntityManager instead of Jdbc and SpringTests with Junit 4 but the principle is the same.

There are some rules for my data fixture:

  • The data must not be inserted using the DAO as this means your relying on the DAO under test.  I used the entity manager providing direct insert statements and just executed the sql.
  • The data inserted must be driven from some immutable holder objects that encapsulate that data.  This provides a robust test and some handy containers that are used later in the test.  This also means that the test has no need whatsoever to know anything about the data!  It just needs to make assertions that the data holders contain the same data as the model returned from the dao.
  • Assert that default values are not used.  I have seen bugs because ’0′ was used as data in long type identifiers, then it turns out the code never set the variable.  My data holders validate their invariants.
  • The fixture should be responsible for stringing together primary and foreign keys, avoiding problems with uniqueness etc.  I.e the fixture is aware of the database requirements.
  • The fixture should allow for several generations of itself to be created.  Thus from the test, several rows of data and the associated graph can be created to provide a rich enough set.  Rich enough to provide good data coverage without being complex or slow.
  • Finally labels, names and other data should be identifiable, this makes it easier to debug if required.

So with careful thought, use of the builder pattern for the main fixture, immutable object design and a hierarchy of data and object creation I arrive at the finished product.  The design is strong, the test works and it satisfies the above requirements, but I am not happy.

It took too long to hand craft all this code.  The fixture is big although easy to understand.  There has to be a better way.  So I look for an alternative.  What I want is something that provides the above as far as possible but lighter.  I envisaged, something where the data is configurable and you don’t have to write the complex fixture at all.

Unitils a possible shortcut

Quoting from the Unitils site: Unit tests for the database layer can be extremely valuable when building enterprise applications, but are often abandoned because of their complexity.
How true that is!

So my search took me through the links at the bottom of the page, until I hit upon Unitils: http://unitils.org/cookbook.html.  Instead of writing a complex fixture to encapsulate the data safely you write XML that describes the data needed for a given test.  This is associated with the class or method using a naming strategy, although this can be overridden by specifying files.  This could be handy if one dataset fits several test classes.  This is already easier than writing actual insert statements and can be achieved easily with the database schema browser and a text editor.  I am not interested in the mocking or database setup described there, whilst useful I had solved most of those issues with spring.  In fact unitils uses spring under the hood, so is probably using the same facilities I do in my work.  Unitils provides some powerful aid with the assertions in the test too, take a look at this!

ReflectionAssert

The reflection assert class provides a toolkit that addresses the kind of problems you often run into when making assertions against the data a DAO returns.  For instance lets say you have a model object with a primary key value.  This field is often hidden from you, and may not be used in the equals method.  In fact the equals methods on model objects are not always implemented or have some business specific implementation, so you can’t rely on them in the tests.  My solution was to use my own holder objects for data, and then to write my own validation routines against the model object.  Time consuming!

Take a look at these examples taken directly from the tutorial, (thanks guys!)

import static org.unitils.reflectionassert.ReflectionAssert.*;

// Exact field-by-field comparison
assertReflectionEquals(
  new Person("John", "Doe", new Address("New street", 5, "Brussels")),
  new Person("John", "Doe", new Address("New street", 5, "Brussels"));

// Ignore Null / 0 values in the expected object
assertReflectionEquals(
  new Person("John", null, new Address("New street", 0, null)),
  new Person("John", "Doe", new Address("New street", 5, "Brussels"),
ReflectionComparatorMode.IGNORE_DEFAULTS); 

// Ignore collection order
assertReflectionEquals(
Arrays.asList(
  new Person("John"), new Person("Jane")),
  new Person[] {new Person("Jane"), new Person("John")},
ReflectionComparatorMode.LENIENT_ORDER);

// Ignore null/0 values + collection order
assertLenientEquals(
  Arrays.asList(
  new Person("John"), null),
  new Person[] {new Person("Jane", "Doe"), new Person("John", "Doe")});

// Check only the firstName property
assertPropertyLenientEquals("firstName", Arrays.asList("John", "Jane"),
  new Person[] {new Person("Jane", "Doe"), new Person("John", "Doe")});

Comments on the assertion example

  • Note the way its possible to ignore null and defaults like ’0′ and certain fields.  This should mean I can use constructors on my model and not have to worry about keys.  These are tested because you get data back in the child tables anyway.
  • Note the system for ignoring order.  I use google multiset but this is even simpler than that.

General Problems that might still arise

I am not sure if my model is always going to be so accommodating.  I hope that visibility of appropriate constructors will allow the assertions I want to make via reflection.  I guess there is little impact in adding more for testing, but my design meant I did not have to worry about it because my assertions are made against an object provided by the fixture.  In fact the design of this object was to facilitate creation without complex constructors.  A constraint the model does not have to worry about.  i.e Only the DAO needs to create a model object  This also had the advantage of reducing the failure to one point, the DAO.  In the system above, you type data into the XML and into the test, so its possible to miss the match and the test will fail.

It will be interesting to see if its possible to work with nested objects and complex graphs. JPA or currently Hibernate is knitting things together so it may not be possible to create the objects required.  Its going to take a bit longer to finish this off, so I will explore further and post up my finished result later on.

If I had a request it would be for this framework to load a model object off the xml defining the data, and have an assertions like this:

public void validateModelLoadedFromTestDataAgainstReturnedModel(T y)

This would close the loop would it not?

Links

http://www.dallaway.com/acad/dbunit.html

http://www.theserverside.com/tt/articles/article.tss?l=UnitTesting

http://unitils.org/tutorial.html#Testing_with_JPA

http://www.theserverside.com/tt/articles/article.tss?l=UnitTestinghttp://www.theserverside.com/tt/articles/article.tss?l=UnitTesting
Digg This
Reddit This
Stumble Now!
Buzz This
Vote on DZone
Share on Facebook
Bookmark this on Delicious
Kick It on DotNetKicks.com
Shout it
Share on LinkedIn
Bookmark this on Technorati
Post on Twitter
Google Buzz (aka. Google Reader)

Comments are closed.