Unit testing persistence with template methods

Now that we have techniques for making sure our tests hit the database, what are we going to test? Load by ID, load by name, load all, save, delete – sound like a reasonable starting point? I also throw in tests for what I call “finders”, which are utility classes for loading objects by anything other than primary or natural keys. Essentially these are DAOs, but I call them finders because I use the same idea for non-persistent objects as well. I must emphasise again – I’m only trying to exercise my persistence layer here. Domain-specific behaviour is tested in a different test suite.

One more concept before we get down to tin tacks. All my domain objects are accessed via a “manager” classes, which have methods like – you get guessed it – load by ID, load by name, save, delete, etc. The key to this approach to test generation is there’s a high level interface for managers, an interface for finders, and another one for domain objects. The specifics aren’t as important as the fact these interaces exist.

OK, with all of that, the test method for loading a generic ObjectType by ID looks like this:

public abstract class BasePersistenceTests extends BaseContextTests {
	@Test
	public void testGetByID() {
		
		ObjectType domainObject2 = getChildObjectManager().get(domainObject.getId());
		
		if (hasTextIDName()) {
			assertEquals(testObjectName, getTextIDValue(domainObject2));
		}
		assertEquals(domainObject.getId(), domainObject2.getId());

	}
}

As you can see, this is a method on an abstract base class. It’s the same for every test class for every domain object. Obviously there’s a bit more going on here, so let’s go through that.

getChildObjectManager() is the only piece that is different for each domain object under test. It specifies the the manager for this object, and once that’s provided our base classes can use our standard interfaces for everything they need to do.

domainObject was set up in our pre-test transaction that we talked about previously. Still within this same BasePersistenceTests class, we have:

	protected ObjectType domainObject;
	protected String testObjectName = "testObj";

	protected void doSetupBeforeTransaction() {
		domainObject = createChildObject();
	}
	protected ObjectType createChildObject() {
		return getChildObjectManager().create(testObjectName);
	}

doSetupBeforeTransaction() is invoked by our pre-test transaction setup code. Once again, identical for every domain object test class. In true template method style, we could override either or both of these methods for more exotic cases – for example, when we need to supply more inputs to the create() method. Although it’s amazing for how many domain objects you can create an instance with sensible defaults just by specifying a name.

hasTextIDName() and getTextIDValue(): I won’t go into the gory details. Usually hasTextIDName() just returns true (you can override it to return false), and getTextIDValue() uses reflection to look for a special annotation and get the value of the annotated field.

getID() is defined by the base domain object class – pretty obvious what this does.

So, our minimal test class looks like this:

public class myObjectPersistenceTests extends
		myPackagePersistenceTests {

	@Override
	protected DomainObjectManager getChildObjectManager() {
		return myServiceSingleton.getMyObjectManager();
	}

}

That’s the whole thing – only the imports left out for brevity. I get 11 standard persistence tests – the getByID I showed you above, plus a few more I didn’t show – for half-a-dozen lines of code.

This simplest case is for an aggregate root. Things get slightly more complex for child objects within an aggregate, and more complex again for association objects. I top out at about a dozen overridden primitive operations, for about 60 lines of code in the test class, but get a couple of extra tests for testing things like cascades.

Now, obviously, if you’d read this far, I haven’t left you with any runnable code. For you to run my code you’d need to have your domain model set up just like mine, with all the same base classes and interfaces, and that’s not very likely, is it? At some point I might publish my base classes and interfaces, and you can port your code base over to my system ;). In the meantime, the take-home message is:

  • You can and should test your persistence layer/mappings, as well as domain object behaviour
  • Persistence layer testing has overheads and fragilities not needed by behaviour testing, so do it in a separate test suite
  • A decent set of base domain classes makes it possible to write generic base test classes
  • Use of the template method pattern means you only write the standard stuff once – and there’s a lot of standard stuff
  • All of which makes life much less boring 🙂

Previous posts:
Spring/Hibernate unit testing part 2
Spring/Hibernate unit testing

Leave a Reply

Your email address will not be published. Required fields are marked *