Using db4o for unit test results verification

By nailsandhammers

There was a situation when I needed to verify status of an object that has been supposedly created by the component I was testing, but I couldn’t reach it from the TestCase. It was not being returned by the method, nor it was persisted in any way I could easily use. It existed only for a fraction of a second and then it was sent to another component/class. This is pretty common when you have a system that is doing any kind of remote calls or asynchronous messaging. In my case I was working on an CEP application.

Of course there are several refactoring solutions that might help,  but Gerard Meszaros in his xUnit Test Patterns describes another way how to deal with this situation – a TestSpy pattern. TestSpy is a class that I can ‘inject’ to the tested class in order to instrument and capture its behaviour. I used the TestSpy to persist objects that were being created in the tested method so I can verify them later.

I had wide number of choices how to implement the persistence – using a file, a regular sql database, embedded database like HSQLDB or H2.  I really wanted something simple, no extra server administration, table creation or  O/R mapping definition. I wanted to store and retrieve objects, so I decided to give it a try with an embedded object-oriented database . I chose db4o for no particular reason except that it seems to be pretty well established as a product and is coming with an open source license (GPL, though).

Despite having not much of former exposure to db4o, it turned out pretty quick and simple. I followed the tutorial and I had the whole testing up and running in about 30 minutes.

Here is the ‘TestSpy’ class:


public class TestStore {
	private static String dbFileName = "DB4O.DATA";
	private ObjectContainer	db;

	public TestStore() {
		db = Db4o.openFile(dbFileName);
	}

	public void close() {
		db.close();
	}

	// store object to the database
	public void store(Object dataObj) {
		db.store(dataObj);
	}

	// retrieve stored objects using an example object
	public List queryByExample(Object example) {
		ObjectSet resultSet = db.queryByExample(example);
		return resultSet;
	}

	// retrieve stored objects of the same class
	public List queryByClass(Class clazz) {
		ObjectSet resultSet = db.queryByExample(clazz);
		return resultSet;
	}
}

That’s it, that’s all the db4o-specific code I needed.

And here is the tested method:


public class EmailSender {

	// send emails to all recipients using the NewYearGreeting email template
	public void sendNewYearGreetings(String[] recipients) {
		for(int i=0; i<recipients.length; i++) {
			NewYearGreetingEmail email = new NewYearGreetingEmail(recipients[i]);
			smtpServer.send(email);

			//testability support
			if(AppConfig.ALLOW_TEST_SPY) {
				testSpy.store(email);
			}
		}
	}

	private TestStore testSpy;
	//set the TestSpy
	public void setTestSpy(TestStore testStore) {
		testSpy = testStore;
	}
}

Finally, here the testing and verification code from the TestCase:


public void testSendNewYearGreetings() {
	try {
		String[] recipients = new String[]{"joe's_addr", "bob's_addr",
								"ann's_addr"};
		TestStore testStore = new TestStore();
		EmailSender emailSender = new EmailSender();
		emailSender.setTestSpy(testStore);
		emailSender.sendNewYearGreetings(recipients);

		List resultSet = testStore.queryByClass(NewYearGreetingEmail.class);
		if(resultSet.size()!=3)
			fail("Unexpected number NewYearGreetingEmail objects!");
		//TODO - add further verification
	}
	catch(Exception ex) {
		fail("testSendNewYearGreetings Failed");
	}
}

Of course, this testing method changes the tested code somewhat. As usual in this world, there is no free lunch and adding more testability to the code introduces some features that may need to be removed in production. If this is an issue, then you can use some of the ‘conditional compilation’ techniques :

  • use ant to remove the test code from the production code as described for example here.
  • you can even use the gcc preprocessor to have ‘real’ conditional compilation as described here.
  • or you can use a static final variable that drives the execution of the test logic. This feature takes the advantage of the fact that the compilers do not generate unreachable code, so in fact your class would not even contain calls to the Test Spy methods. See the AppConfig.ALLOW_TEST_SPY variable being used in the code above.

In this case the OODB did exactly what I needed – allows me to store and retrieve my objects without too much dancing around. Pretty clean and easy to set up. I will see if this is the right hammer for the testing nail.

2 Responses to “Using db4o for unit test results verification”

  1. Miro Says:

    That’s a great idea. Thanks.

  2. Using NeoDatis OODB for unit testing « Nails And Hammers Says:

    [...] NeoDatis OODB for unit testing By nailsandhammers Related to the previous blog entry about using db4o for unit testing – I tried an alternative framework NeoDatis, which is a small and [...]

Leave a Reply