Unit testing data access components

When it comes to unit testing data access components, some people would be able to debate the exact definition or a unit test. Some say that a unit test that involves database related code must communicate with the database in order to be a true unit test. Others say that a unit test that communicates with the database is an integration test, not a unit test. What do you think? Well, here is what Michael Feathers thinks, as quoted in his book “Working Effectively With Legacy Code” (Addison-Wesley, 2005)

A test is not a unit test if:

-It talks to the database
-It communicates across the network
-It touches the file system
-It can’t run at the same time as any of your other unit tests
-You have to do special things to your environment (such as editing config files) to run it.

Tests that do these things aren’t bad. Often they are worth writing, and they can be written in a unit test harness. However, it is important to be able to separate them from true unit tests so that we can keep a set of tests that we can run fast whenever we make our changes.

The challenges of testing data access components

What makes testing code that accesses data more challenging than testing other parts of the application? Data access code involves more layers of application and system architecture. You have application logic, which talks to persistence logic, which uses a persistence framework like Hibernate, and then there is the JDBC driver which connects to a relational database. In other words, there are a lot of dependencies which add uncertain variables to our testing. If we can’t isolate these dependencies then we can’t be sure that all tests will always be executed under the same conditions.

We don’t always want to rely on systems that might not be available during the time of testing. Database testing often requires a delicate setup of a data test environment which means creating a test database, and careful attention to the construction and destruction of test data. That requires time. As the application evolves, the database changes, tables are added and modified, so the test scripts that you wrote to import data might not work anymore. These are all things to consider when writing tests that communicate directly with the database.

Another more obvious point to be aware of is that tests not only need to be independent of database conditions, but they also need to be fast! Very fast. If all the unit tests are automatically run before an application is deployed, which is often the case, then having tests communicate too much with the database could slow down deployment. It can also slow down development if developers need to rely on test times to proceed with writing new code.

Revisiting the DAO pattern

The Data Access Object design pattern, as originally defined in the book “Core J2EE Patterns” (Deepak Alur, Dan Malks, and John Crupi; Addison-Wesley, 2003) and described in the wikipedia: the data access object (DAO) is an object that provides an abstract interface to some type of database or persistence mechanism, providing some specific operations without exposing details of the database. In other words, for each persistent domain object, there is a corresponding interface and implementation(s) for persisting the object. The object could be persisted in a number of places and ways: to a database or a file system for example, via a persistence framework or plain old JDBC code.

The DAO pattern comes in handy in many ways and although I already talked about it in the Persistence with Hibernate & Spring tutorial, I will talk about how it helps us with testing now. Sometimes might want to test parts of our application involving data access code, that have not been implemented yet. In other cases we would like to have the flexibility of swapping implementations as we have done below. Suppose aren’t quite sure whether we would like to use Hibernate or raw JDBC to connect to the database, so we decide to create two implementations of the interface GreetingDao: HibernateGreetingDao and JdbcGreetingDao. This way, layers above the DAO can invoke persistence logic without knowing or caring where the objects came from or how they are stored. It can be useful to test drive the design of your application code as all these ideas are uncertain, you don’t really have a database yet, but you want to start coding the persistence layer.

GreetingDao.java
public interface GreetingDao {
 
	public List<Greeting> getAllGreetings();
 
	public void addGreeting(Greeting greeting);
 
}
 
public class HibernateGreetingDao implements GreetingDao {
 
   ...
 
}
 
public class JdbcGreetingDao implements GreetingDao {
 
   ...
 
}


Unit testing HibernateDao without connecting to the database

Now let’s have a look again at our HibernateGreetingDao class. We will use Mockito to create a mock Session, SessionFactory, and Query object. Then use Mockito to ensure that the mock objects are returned when actual calls are made.

The only thing that was missing from the HibernateGreetingDao class we had before, was a way to inject a fake SessionFactory into our HibernateGreetingDao class during testing. So I added the method “setSessionFactory” which you can see below. It is also possible to do this with xml configuration files, so that is something you should consider as your application grows.

HibernateGreetingDao.java
package com.bitbybit.dao;
 
import java.util.List;
import org.apache.log4j.Logger;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.bitbybit.domain.Greeting;
 
@Repository("hibernateGreetingDao")
public class HibernateGreetingDao implements GreetingDao {
 
	protected static Logger logger = Logger.getLogger("service");
 
	public HibernateGreetingDao() {}
 
	@Autowired
	private SessionFactory sessionFactory;
 
	//NOTE this is a new method and has been added to make testing easier!
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}
 
	public List<Greeting> getAllGreetings() {
		Session session = sessionFactory.getCurrentSession();		
		Query q = session.createQuery("select g from Greeting g order by id desc");
		List<Greeting> greetingList = q.list(); 
	        return greetingList;			
	}
 
	public void addGreeting(Greeting greeting) {
		Session session = sessionFactory.getCurrentSession();	
		session.save(greeting);
	}
 
}

There is just one test for now, and it might appear that this test does not have much sense. You are right for thinking this. It creates an expected list of 3 greetings, ensures that when query.list() is called, the expected list is returned, and then calls hibernateGreetingDao.getAllGreetings() which returns the “actual” list. The two lists are compared, and of course they are the same.

package com.bitbybit.dao;
 
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import static org.mockito.Mockito.*;
import org.hibernate.*; 
import org.hibernate.classic.Session;
import junit.framework.TestCase;
import com.bitbybit.domain.Greeting;
 
public class HibernateGreetingDaoTest extends TestCase {
 
	private SessionFactory sessionFactory; 
	private Session session; 
	private Query query;
 
	@Before 
	public void setUp() {
		sessionFactory = mock(SessionFactory.class); 
		session = mock(Session.class); 
		query = mock(Query.class);
	}
 
	//testing hibernate and the method getAllGreetings().
	//create an expected list, use Mockito to return expected list upon query.list().
	//verify that the expected list = the actual list from hibernateGreetingDao.getAllGreetings().
	@Test
	public void testTheMethodGetAllGreetingsShouldReturnAListOfGreetings() {
		//GIVEN
		String hql = "select g from Greeting g order by id desc";
		List<Greeting> expectedGreetingList = new ArrayList<Greeting>();
		expectedGreetingList.add(new Greeting("Welcome to the world!", new Date(), "johnnyd"));
		expectedGreetingList.add(new Greeting("Hello there everyone...", new Date(), "mrsam"));
		expectedGreetingList.add(new Greeting("Hey there", new Date(), "sonialawson"));
		//WHEN
		HibernateGreetingDao hibernateGreetingDao = new HibernateGreetingDao();
		hibernateGreetingDao.setSessionFactory(sessionFactory);
		when(sessionFactory.getCurrentSession()).thenReturn(session);
		when(session.createQuery(hql)).thenReturn(query);
		when(query.list()).thenReturn(expectedGreetingList);
		List<Greeting> actualGreetingList = hibernateGreetingDao.getAllGreetings();
		//THEN
		assertNotNull(actualGreetingList);
		assertSame(expectedGreetingList, actualGreetingList);		
	}
 
}


So, is there really a point to writing tests like this? That all depends on how you test drive your application. In this case, I am just showing you that it is possible to test data access code without actually connecting to the database. So here is how to do it. It might or might not make sense for you to write tests like this, so that is something that you will have to evaluate, depending on your development and testing conditions.




0 Comments

Post a Comment

(required):