Monday, June 6, 2011

Unit Testing Hibernate Data Access Objects using JUnit 4 - Part I

In this article, I want to show you how to write unit tests for your DAOs. You would preferably use an in-memory db instance like HsqlDb but using a test db is perfectly fine since you're going to rollback each db transaction. One thing to remember while doing a DAO unit test is that you'd want to test with the db provider that you are going to use in the live, except you will use a test db instance and not the live db instance. The reason for this is that let's say you are using Hibernate just like I am doing here. You would want to test whether or not the sql queries run against the db using the specific version of hibernate works. Hibernate ships with different versions of driver classes for different db vendors, but for some reason, let's say the encoding of the db you're going to use in the live version does not support certain SQL queries generated by Hibernate. If you do an in-memory HSQLDB test and pass you will most likely think that will work with your specific version of db provider. I just don't think this is accurate enough especially if your queries are complex joins. Again, the rollback feature works for you to take advantage of and regardless of whether you are using an in-memory instance or not, you would still need to populate some data before testing. How else would you test find methods? Another advice I would like to give is to try and use accurate data. I don't mean real-values of credit cards, but data not like "AAAA" in place of a person's name. You may run into various issues later when populating your test db with such data. One such problem I can think of is if your entities are annotated with column specifications such as length and type and you have added data that may not be 100% compatible with that. Another problem is relationships between entities.

Moving on we will have these steps:

1. Pre-requisites
2. Setting up the application context
3. Writing Domain and DAO interface
4. Writing DAO unit tests
5. Writing DAO implementations

I will cover 1 and 2 in this part to have the framework in place. In the next part we will write our domain (just one) and dao interface, dao unit test and then dao implementation. This is a logical order because we would want to test first an then see what we need in order for the test to pass. That 'what we need' will go into our implementation. This is called, as you might have guessed it, Test Driven Approach.

Pre-requisites

* Spring Core library for dependency injection. We are also going to use SpringJunit4ClassRunner for unit testing.
* Hibernate 3.x. We will be using Hibernate's Criteria, specifically, Detached Criteria. For more info on using Criteria look here.
* MySQL db.

You can use Maven to configure all of these. Here's what part of the pom.xml looks like. If you need more help on configuring a maven project please look at my "How to setup a Maven Java Enterprise Application". You can find that under the category "Deployment". Here's the list of artifacts you'll need:

<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>3.3.2.GA</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-annotations</artifactId>
<version>3.3.1.GA</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-commons-annotations</artifactId>
<version>3.3.0.ga</version>
</dependency>
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.6.0.GA</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jcl</artifactId>
<version>1.5.8</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.16</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.framework.version}</version> <!--version 3.0.5.RELEASE -->
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.framework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.framework.version}</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
</dependency>


Setting up the Application Context

When the test is run, it will scan the application context to inject the dao interface. The implementation of the dao interface will use Hibernate's sessionFactory to run our Hibernate queries. We will also add our single Item domain/entity to use sessionFactory. That object will be directly mapped to the ITEM table of our db. I will not create the table since this is simple enough. Lastly, we will need to use Transactions in order to rollback our unit test methods. For this, we will annotate our test methods as @Transactional. Below is the applicationContext.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"  xmlns:tx="http://www.springframework.org/schema/tx"     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

<!--  This is where the properties related to datasource are read from -->
<bean id="propertyConfigurer">
<property name="location" value="classpath:hibernate.properties" />
<property name="ignoreUnresolvablePlaceholders" value="false" />
</bean>

<!--  Define dataSource to use -->
<bean id="dataSource">
<property name="driverClassName" value="${hibernate.jdbc.driver}" /> <!-org.gjt.mm.mysql.Driver -->
<property name="url" value="${hibernate.jdbc.url}" />
<property name="username" value="${hibernate.jdbc.user}" />
<property name="password" value="${hibernate.jdbc.password}" />
</bean>

<!--  The sessionFactory will scan the domain objects and their annotated relationships. -->
<bean id="sessionFactory">
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>
<value>com.company.application.core.domain.Item</value>
..............
</list>
</property>
<property name="schemaUpdate" value="true" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.connection.isolation">2</prop>
<prop key="hibernate.bytecode.use_reflection_optimizer">true</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.jdbc.batch_size">20</prop>
<prop key="hibernate.max_fetch_depth">2</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.format_sql">true</prop>
</props>
</property>
</bean>

<!--  Define Transaction Manager. We will use Hibernate Transaction Manager. -->

<bean id="transactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!--  We will set transactional properties with annotation -->
<tx:annotation-driven />
<bean id="itemDao">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>


One thing you might have noticed is that I could easily annotated my Dao as @Resource, have it scanned and not defined in the xml above. That is perfectly legal. Now we're set to write our domain, dao interface, dao test and dao implementation.

6 comments:

  1. Hi there, You have done a fantastic job. I'll certainly digg it and individually recommend to my friends. I am confident they'll be benefited from this web site.

    ReplyDelete
  2. i bookmarked you in my browser admin thank you so much i will likely be searching for your upcoming posts

    ReplyDelete
  3. Thanks like your Unit Testing Hibernate Data Access Objects using JUnit 4 – Part I 20k lines and refactoring

    ReplyDelete
  4. Your way of describing everything in this article is really fastidious, all be able to effortlessly know it, Thanks a lot.

    ReplyDelete
  5. I think this is among the most important info for me. And i am glad reading your article. But want to remark on few general things, The web site style is ideal, the articles is really nice : D. Good job, cheers

    ReplyDelete