Thursday, July 12, 2012

Spring MVC + Hibernate: testing the persistence layer (DAOs) with TestNg

Whenever you're working on a Spring MVC project, you might want to unit tests each single layer of your application. In this post, we'll see how we can perform unit testing on the persistence layer, using TestNg. More specifically, we'll go through unit testing a persistence layer that is built upon a DAO pattern, implemented using Hibernate.

DAO classes
The DAO classes that we are going to test in this example are structured like this:


As general CRUD methods are implemented in the GenericDaoImpl abstract class, we'll consider that our UserDaoImpl class is empty, in order to keep things simple:

@Repository
public class UserDaoImpl extends GenericDaoImpl<user, long=""> implements UserDao {
 // methods specific to this concrete DAO
}



As you can see, this class is annotated with @Repository, which registers it as a "Repository" type Spring bean.

Spring configuration

<?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"
 xmlns:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">

 <context:component-scan base-package="be.glimmo">
 
 <!-- Enabling annotation-driven transaction management -->
 <tx:annotation-driven />

 <!-- Hibernate SessionFactory -->
 <bean id="sessionFactory"
  class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="hibernateProperties">
   <props>
    <!-- Use the C3P0 connection pool provider -->
    <prop key="hibernate.c3p0.min_size">5</prop>
    <prop key="hibernate.c3p0.max_size">20</prop>
    <prop key="hibernate.c3p0.timeout">1800</prop>
    <prop key="hibernate.c3p0.max_statements">50</prop>
    <prop key="hibernate.c3p0.idle_test_period">300</prop>
    <prop key="hibernate.dialect">org.hibernate.dialect.DerbyDialect</prop>
    <prop key="hibernate.hbm2ddl.auto">create-drop</prop>

    <!-- Show and print nice SQL on stdout -->
    <prop key="show_sql">true</prop>
    <prop key="format_sql">true</prop>
   </props>
  </property>
  <property name="packagesToScan" value="be.glimmo.domain" />
 </bean>

 <!-- Defining a transaction manager bean (used for annotation-driven transaction 
  management -->
 <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
  <property name="sessionFactory" ref="sessionFactory" />
  <property name="dataSource" ref="dataSource" />
 </bean>

 <!-- Defining the Datasource bean -->
 <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
  <property name="driverClassName" value="org.apache.derby.jdbc.ClientDriver" />
  <property name="url" value="jdbc:derby://localhost:1527/glimmo-testdb" />
  <property name="username" value="glimmo" />
  <property name="password" value="password" />
  <property name="minIdle" value="5" />
  <property name="maxActive" value="10" />
  <property name="defaultAutoCommit" value="false" />
 </bean>
</beans>

An important thing here: make sure you specify a "dataSource" bean. Although we would usually declare a bean with the ID "dataSource", when working with Spring, just know that it is also possible to have Spring properly configured without this kind of bean. Nevertheless, configuring unit testing with TestNg simply requires us to declare this bean. More (we'll see why, later).

TestNg test class
Now, let's take a look at an example of test class that will allow us to test each single method of our DAO:


@Test
@ContextConfiguration(locations={"classpath:Glimmo-context-test-configuration.xml", "classpath:Glimmo-persistence-test-configuration.xml"})
public class TestUserDaoDummy extends AbstractTransactionalTestNGSpringContextTests{
 
 // TestNg Data Providers
 Object[][] users = new Object[][]{
   new Object []{"user1", "user1@test.com", "user_firstname", "user_lastname", 
     UserGrade.ADMIN, "password", null}
 };
 
 @DataProvider(name="usersProvider")
 public Object[][] provideUsersForCreation(){
  return users;
 }
 
 @Autowired
 private UserDao userDao;
 
 @Test(dataProvider="usersProvider")
 @Rollback(false)
 public void testUserCreation(String username, String email, String firstName, String lastName, UserGrade userGrade, String password, Date gradeEnd) {
  User newUser = new User(username, email, firstName, lastName);
  newUser.setGrade(userGrade);
  newUser.setPassword(password);
  userDao.save(newUser);
  
  Assert.assertNotEquals(super.countRowsInTable("USERS"), 0);
 }
}



There are 2 important things to pay attention to, here:

  • Have the test class extends AbstractTransactionalTestNGSpringContextTests
The AbstractTransactionalTestNGSpringContextTests class helps you manage the transactional aspects when testing the methods from the DAO class (as a matter of fact, it sets the @Transactional annotation at class level, so that you don't need to worry about transactions when writing your test methods). It also provide convenience methods (based upon the underlying SimpleJdbcTemplate instance...) to query the DB, such as executeSqlScript(...) and countRowsInTable(...). This class expects a bean with the ID "dataSource". That's the reason why I said on the previous section that our Spring configuration has to specify such a bean.



  • Annotate your test class with @ContextConfiguration
This annotation is provided by Spring for test purposes and allows you to load Spring configuration file. This annotation supports several ways to specify which configuration to load but its "locations" attribute is likely the easiest and most used one: we just have to provide a comma separated list of String values. Each of them , referring to a Spring XML configuration file that needs to be loaded in order to initialize the Spring application context. In the example, I prefixed each reference with "classpath:" meaning that the configuration files will be retrieved from the classpath of my project (since I'm working on a Maven based project, I simply put them in the src/test/resources directory).

Now, you're able to wire the DAO classes (as long as they're registered as Spring beans) into you test class, and test every single of their methods. VoilĂ !

No comments:

Post a Comment