Friday, June 22, 2012

Writing JUnit4 tests - Spring 2.5 Hibernate 3.0 configuration

This article is about creating a simple JUnit4 test case for a DAO class. I'm using Spring and Hibernate configured environment for my application -CeylonCuisine

Technologies used:
Java 6
JUnit4
Spring 2.5
Hibernate 3.0
Maven 3.0

UserDao has one method to inserts a User entity to the USERS table – addUser().

package com.ceyloncuisine.dao;
public interface UserDao {

    public void addUser(User user);

}

To keep this simple, only the interface method is shown.
When writing the test case, it’s always good to have a base test class which we can store all the common methods and utility methods. This class I’ll name as BaseTest.

package com.ceyloncuisine.base;

import org.hibernate.SessionFactory;
import org.junit.Ignore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.transaction.TransactionConfiguration;

@Ignore
@ContextConfiguration(locations = "/test-spring-config.xml")
@TransactionConfiguration(transactionManager = "txManager")
public class BaseTest extends AbstractTransactionalJUnit4SpringContextTests {

       @Autowired
       SessionFactory sessionFactory;

       protected int queryForInt(String query) {
              return simpleJdbcTemplate.queryForInt(query);
       }

       protected void flush() {
              SessionFactoryUtils.getSession(sessionFactory, false).flush();
       }

}


We need to extend AbstractTransactionalJUnit4SpringContextTests. Using annotations will make things easy for you.

@Ignore – I don’t need this class to run as a test case, this is only used as a base class. So @Ignore annotation will make sure this will be ignored at test run.

@ContextConfiguration – This is how to load the application context for tests.  We need to have a separate Spring configuration that will be used for tests.  You have to place this configuration file in src\test\resources folder. I will include the test configuration file later in this article.

@TransactionConfiguration – We need to provide transactional support for testing. This is where to specify the transaction manager we’ll be using.

By default the transaction manager name is called “transactionManager”, if you have some other name for your transaction manager, as in this example, you have to specify the name. “txManager” is defined in the ‘test-spring-config.xml’ file.
sessionFactory’ is configured in the test-spring-config.xml file. 

I have two methods flush() and queryForInt() which I thought to include in the base class as these will be reused by all Dao classes.

Now let’s see the test class. UserDaoTest

package com.ceyloncuisine.dao;

import junit.framework.Assert;

import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

import com.ceyloncuisine.base.BaseTest;
import com.ceyloncuisine.domain.User;

public class UserDaoTest extends BaseTest {

       @Autowired
       UserDao userDao;

       @Test
       public void testAddUser() {

              String query = "select count(*) from users where fname = 'bob'";

              User user = new User();
              user.setFname("bob");

              userDao.addUser(user);

              // flush the session to get the inserted record
              flush();

              int count = queryForInt(query);

              Assert.assertEquals("User was not inserted to the DB", 1, count);
       }

}

The inserted record will be removed from the database when the test case completes. Therefore the database remains clean and you don’t have to bother about the data inserted while testing.

The @TransactionConfiguration setting in the base test class has an attribute ‘defaultRollback’. The value of this is ‘true’ by default, means the transaction will be rolled back at the end. If you set this to ‘false’, test data inserted to the database will not be removed.
TransactionConfiguration(transactionManager = "txManager", defaultRollback=false)

This is the Spring configuration file used for tests.
test-spring-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

<bean name="userDao" class="com.ceyloncuisine.dao.impl.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>com/ceyloncuisine/hibernate/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.MySQLDialect
</prop>
              </props>
       </property>

       </bean>

       <bean id="dataSource"
              class="org.springframework.jdbc.datasource.DriverManagerDataSource">
              <property name="driverClassName" value="com.mysql.jdbc.Driver" />
              <property name="url" value="jdbc:mysql://localhost:3306/ceyloncuisine" />
              <property name="username" value="root" />
              <property name="password" value="sa" />
       </bean>


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

</beans>

test-spring-config.xml defines the sessionFactory  and the dataSource beans required to communicate with the MySQL database.  sessionFactory defines the hibernate mapping file for the User entity. (User.hbm.xml)
A HibernateTransactionManager is used to handle transactions and it is defined as “txManager”.

Below is the pom.xml file used to configure dependencies with Maven.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
       <modelVersion>4.0.0</modelVersion>
       <groupId>CeylonCuisine</groupId>
       <artifactId>CeylonCuisine</artifactId>
       <version>0.0.1-SNAPSHOT</version>
       <packaging>war</packaging>
       <name>CeylonCuisine</name>

       <repositories>
              <repository>
                     <id>Java.Net</id>
                     <url>http://download.java.net/maven/2/</url>
              </repository>

       </repositories>

       <dependencies>

              <!-- Spring framework -->
              <dependency>
                     <groupId>org.springframework</groupId>
                     <artifactId>spring</artifactId>
                     <version>2.5.6</version>
              </dependency>

              <dependency>
                     <groupId>org.springframework</groupId>
                     <artifactId>spring-hibernate3</artifactId>
                     <version>2.0.8</version>
              </dependency>

              <dependency>
                     <groupId>org.springframework</groupId>
                     <artifactId>spring-test</artifactId>
                     <version>2.5.6</version>
              </dependency>

              <!-- Unit Test -->
              <dependency>
                     <groupId>junit</groupId>
                     <artifactId>junit</artifactId>
                     <version>4.10</version>
              </dependency>

              <!-- MySQL database driver -->
              <dependency>
                     <groupId>mysql</groupId>
                     <artifactId>mysql-connector-java</artifactId>
                     <version>5.1.9</version>
              </dependency>

              <!-- Hibernate core -->
              <dependency>
                     <groupId>org.hibernate</groupId>
                     <artifactId>hibernate</artifactId>
                     <version>3.2.7.ga</version>
              </dependency>

              <!-- Hibernate core library dependencies -->
              <dependency>
                     <groupId>dom4j</groupId>
                     <artifactId>dom4j</artifactId>
                     <version>1.6.1</version>
              </dependency>

              <dependency>
                     <groupId>commons-collections</groupId>
                     <artifactId>commons-collections</artifactId>
                     <version>3.2.1</version>
              </dependency>

              <dependency>
                     <groupId>cglib</groupId>
                     <artifactId>cglib</artifactId>
                     <version>2.2</version>
              </dependency>

              <!-- Hibernate query library dependency -->
              <dependency>
                     <groupId>antlr</groupId>
                     <artifactId>antlr</artifactId>
                     <version>2.7.7</version>
              </dependency>

              <!-- ASM library dependency -->
              <dependency>
                     <groupId>asm</groupId>
                     <artifactId>asm</artifactId>
                     <version>3.3.1</version>
              </dependency>

       </dependencies>

</project>

Class hierarchy is shown in the below picture.



So, that’s it. Hope this post will help you to configure your JUnit4 test cases.

No comments:

Post a Comment