Unit Testing with Junit, Spring and Hibernate – Part 2

This is a continuation from my previous post on Spring and MockStrutsTestCase and focus on testing Hibernate DAO’s. As I mentioned in the previous post, Spring framework provides a nifty base class (AbstractTransactionalDataSourceSpringContextTests) that provides automatic transaction rollback, exposing a JDBC template to interact with the DB and auto wiring of beans.

Lets take a simple DAO class that save a User object to the database:

public class UserDaoImpl extends HibernateDaoSupport implements UserDao {
  public void save(User user) {
      getHibernateTemplate().save(user);
  }
}

Now in order to test this you would write a test class as below extending from AbstractTransactionalDataSourceSpringContextTests class.

public class UserDaoTest extends AbstractTransactionalDataSourceSpringContextTests {
  private UserDao userDao;
  private SessionFactory sessionFactory = null;

  protected String[] getConfigLocations() {
      return new String[]{"test-spring-config.xml"};
  }

  /**
   * Spring will automatically inject UserDao object on startup
   * @param userDao
   */
  public void setUserDao(UserDao userDao) {
      this.userDao = userDao;
  }

  /**
   * Spring will automatically inject the Hibernate session factory on startup
   * @param sessionFactory
   */
  public void setSessionFactory(SessionFactory sessionFactory) {
      this.sessionFactory = sessionFactory;
  }

  /**
   * Test the save method
   *
   */
  public void testSave(){
      String query = "select count(*) from user where first_name = 'Firstname'";
      int count = jdbcTemplate.queryForInt(query);
      assertEquals("A user already exists in the DB", 0, count);

      User user = new User();
      user.setFirstName("Firstname");

      userDao.saveUser(user);

      // flush the session so we can get the record using JDBC template
      SessionFactoryUtils.getSession(sessionFactory, false).flush();

      count = jdbcTemplate.queryForInt(query);
      assertEquals("User was not found in the DB", 1, count);
  }
}

The test class has to implement the protected String[] getConfigLocations() method from the base class and return a String array of Spring config files which will be used to initialize the Spring context.

UserDao and SessionFactory properties are defined with the setter methods and the base class will take care of injecting them automatically from the Spring context. Auto wiring will not work if there are multiple objects implementing the same interface. In such a case you can remove the setter method and retrieve the object using the exposed applicationContext as below.

   /**
   * Overridden method from base class which gets called automatically
   */
  protected void onSetUpBeforeTransaction() throws Exception {
      super.onSetUpBeforeTransaction();
      userDao = (UserDao) applicationContext.getBean("userDao");
  }

The base class also exposes a JDBC template object (jdbcTemplate) that can be used to query data or setup test data in the database. Note that you need to have a data source and a transaction manager defined in your Spring config in order to use the AbstractTransactionalDataSourceSpringContextTests base class. The data source defined in the config file will be bound to the exposed JDBC template.

In the testSave method first we verify there is no record in the User table where first name equals to ‘Firstname’ using the jdbc template object. Then we call the save method on the UserDao passing it a User object.

Now we simple verify there is a record in the table where first name equals to ‘Firstname’. Before running the query we flush the current Hibernate session to make sure jdbcTemplate can see the newly added record.

Thats it and when the testSave method exits the current transaction will be rolled back and the record inserted to the User table will not be saved. This is great as your test database will always be at a know state at the start and end of a test method.

The spring config file will like below (test-spring-config.xml) :

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>  
    <bean name="userDao" class="com.dao.UserDaoImpl">
       <property name="sessionFactory">
           <ref bean="sessionFactory"/>
       </property>
    </bean>

    <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
       <property name="dataSource">
           <ref bean="dataSource"/>
       </property>
       <property name="mappingResources">
           <list>
               <value>hibernates/User.hbm.xml</value>
           </list>
       </property>
       <property name="hibernateProperties">
           <props>
               <prop key="hibernate.query.factory_class">org.hibernate.hql.classic.ClassicQueryTranslatorFactory</prop>
               <prop key="hibernate.dialect">org.hibernate.dialect.OracleDialect</prop>
           </props>
      </property>
    </bean>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
       <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
       <property name="url" value="jdbc:oracle:thin:@localhost:1521:ORCL" />
       <property name="username" value="test" />
       <property name="password" value="test" />
    </bean>

    <bean id="txManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
       <property name="sessionFactory"><ref local="sessionFactory"/></property>
    </bean>

</beans>

One Reply to “Unit Testing with Junit, Spring and Hibernate – Part 2”

  1. I have to say, that I can not agree with you in 100%, but it’s just my opinion, which could be wrong.
    p.s. You have a very good template . Where did you find it?

Leave a Reply

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