Spring/Hibernate unit testing part 2

The basic Spring transactional testing approach is great for testing domain logic. If you want to test your persistence layer, though, it falls short in some important ways. Most importantly, with the usual approach you will never actually load an object from the database. All the objects you create will hang around in Hibernate’s session cache, and will always be fetched from there. Even objects you created in a @Before method will be within the one Hibernate session.

As an example of why this matters, you can misspell your primary key name in the hibernate mapping file, and these tests will never tell you. Don’t ask me how I know 🙂

The solution is to create some test data before the main test transaction kicks in. That has some significant disadvantages, so I only do it for test classes that are specifically testing the persistence layer. What are the disadvantages?

  • If something goes wrong with your setup or teardown code you can be left with a dirty database and you’ll have to clean up before any further tests will work. In a CI environment that can be a real pain, so ideally you’d be regenerating your test database after each run of the persistence tests.
  • Because you need three transactions per test, it’s significantly slower
  • You have to write the transactional boilerplate for setup and teardown

So, it’s worth doing, but it by no means replaces the basic single-transaction approach. Most of your tests should still be using that approach.

In use, the persistence test class looks something like this:

@ContextConfiguration(locations={"/myApplicationContext.xml"})
public class MyPersistenceTests extends  AbstractTransactionalJUnit4SpringContextTests   {
        @Resource protected SessionFactory sf;
	@Resource protected org.springframework.orm.hibernate3.HibernateTransactionManager txManager;

	@BeforeTransaction
	public void setupBeforeTransaction() {
		DefaultTransactionDefinition def = new DefaultTransactionDefinition();
		def.setName("SomeTxName");
		def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

		TransactionStatus status = txManager.getTransaction(def);
		try {
			// Your setup code here
			txManager.commit(status);
		}
		catch (Exception e) {
			txManager.rollback(status);
			throw new Error (e);
		}
	}
	
	@AfterTransaction
	public void teardownAfterTransaction() {
		DefaultTransactionDefinition def = new DefaultTransactionDefinition();
		def.setName("SomeTxName");
		def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

		TransactionStatus status = txManager.getTransaction(def);
		try {
			// your teardown code here
			txManager.commit(status);
		}
		catch (Exception e) {
			txManager.rollback(status);
			throw new Error (e);
		}

               // Your test methods here
	}

We’re still extending the same Spring base class as before. We’ve just injected our transaction manager and added a couple of methods. The @BeforeTransaction and @AfterTransaction annotations are provided by Spring for exactly this use case. Spring will still wrap our test methods in a transaction automatically, and still roll it back after each method invocation.

So now in our test methods we can load any of the objects we created in our “before transaction” code and be sure we are hitting the database.

After writing 20 or 30 of these persistence test classes you start to notice that they all look very similar. Create an object. Make sure you can load it. Tear it down. This is even more pronounced if you use a base domain object class as I do. In my next post I’ll talk about some techniques for writing abstract test classes that do most of the repetitive stuff.

Leave a Reply

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