Suggested Pages

Tuesday, May 15, 2012

Unit Test with Maven, Spring and HSQLDB - Tutorial

In my programming experience, I have learnt the importance of making unit tests on persistence functionalities. In this post I'll show a way to test persistence functionalities with Maven, Spring and HSQLDB.

HSQLDB


When you develop a software that has to store data, you usually prepare a testing local database. With HSQLDB used in memory mode you can test persistence unit of work in your local machine in easy way. The database is not persistent and exists entirely in random access memory.

Maven - Surefire Plugin


Maven has a lifecycle composed by different phases. During the test phase maven use the the Surefire Plugin to execute the unit tests of an application. You should put your testing code at src/main/test. Then calling mvn clean test you can execute your tests.

Spring - SpringJUnit4ClassRunner


Spring has a package org.springframework.test.context that provides annotation-driven unit and integration test support. It also provides a custom runner class called SpringJUnit4ClassRunner that permit to run your test.

Now Suppose you have a DAO that has only two methods: saveContact and retrieveContact.

ContactDAO.java
package com.simonefolinojavablog.persistence.dao;

import com.simonefolinojavablog.persistence.entity.Contact;

public interface ContactDAO {

 public void saveContact(Contact contact);
 
 public Contact retrieveContact(int idContact);

}



ContactDAOImpl.java
package com.simonefolinojavablog.persistence.dao;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.simonefolinojavablog.persistence.entity.Contact;

@Repository(value = "ContactDAO")
public class ContactDAOImpl implements ContactDAO {

 @PersistenceContext(unitName = "persistenceUnitTest")
 private EntityManager entityManager;

 @Transactional(propagation = Propagation.REQUIRED)
 public void saveContact(Contact contact) {
  entityManager.persist(contact);
 }

 @Transactional(propagation = Propagation.REQUIRED)
 public Contact retrieveContact(int idContact) {
  return entityManager.find(Contact.class, idContact);
 }

}

Contact.java
package com.simonefolinojavablog.persistence.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="Contacts",schema="ExampleSchema")
public class Contact {

 @Id
 @Column
 private int id;
 
 @Column
 private String number;

 public int getId() {
  return id;
 }

 public void setId(int id) {
  this.id = id;
 }

 public String getNumber() {
  return number;
 }

 public void setNumber(String number) {
  this.number = number;
 }

 @Override
 public String toString() {
  return "Contact [id=" + id + ", number=" + number + "]";
 }

}
ContactDAOImpl implements simply the storing and the retrieving of Contact class. Both ContactDAOImpl and ContactDAO are located at src/main/java in Maven's structure.
If you are interested in testing these two functionalities, you can create ContactDAOTest class at src/main/test in Maven's structure, as shown below.

ContactDAOTest.java
package com.simonefolinojavablog.persistence.test;

import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.simonefolinojavablog.persistence.dao.ContactDAO;
import com.simonefolinojavablog.persistence.entity.Contact;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "ContactDAOTest-context.xml")
public class ContactDAOTest {

 @Autowired
 private ContactDAO contactDAO;

 private static Contact contact;
 
 @BeforeClass
 public static void initContact(){
  contact=new Contact();
  contact.setId(1);
  contact.setNumber("22xxxx");
 }

 @Test
 public void testSaveContact() {
  System.out.println("------------testSaveContact(): start -------");
  contactDAO.saveContact(contact);
  System.out.println("------------testSaveContact(): end -------");
 }
 
 @Test
 public void testRetrieveContact() {
  System.out.println("------------testRetrieveContact(): start -------");
  Contact retrieveContact = contactDAO.retrieveContact(contact.getId());
  System.out.println(retrieveContact);
  System.out.println("------------testRetrieveContact(): end -------");
 }
 

}
As you can see ContactDAOTest uses two annotations at class level:
  • @RunWith: tells the runner class that JUnit will invoke instead of the runner built into JUnit;
  • @ContextConfiguration: tells the path where is located application context to load for this test;
Annotations as @Test, @BeforeClass are the same annotations you use in JUnit.

The file shown below is ContactDAOTest-context that represents the application context loaded by the class ContactDAOTest.
In this file, four important beans are declared:
  • dataSource: the datasource c3p0 used to connect to HSQLDB (in memory mode);
  • transactionManager: org.springframework.orm.jpa.JpaTransactionManager;
  • entityManagerFactory: org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean. It has an attribute depends-on="schemaGenerator" which it means that entityManagerFactory depends on the bean schemaGenerator. This bean is called by the container before initializing entityManagerFactory ;
  • schemaGenerator: a bean that creates the schema where entity beans will be stored.

ContactDAOTest-context

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
 xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxrs="http://cxf.apache.org/jaxrs"
 xmlns:cxf="http://cxf.apache.org/core"
 xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd   http://www.springframework.org/schema/beans  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd    http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-3.0.xsd    http://www.springframework.org/schema/jee  http://www.springframework.org/schema/jee/spring-jee-3.0.xsd    http://www.springframework.org/schema/tx   http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

 <!-- annotation support -->
 <context:annotation-config />

 <!-- support for transaction -->
 <tx:annotation-driven />

 <!-- scan package for @Repository annotations -->
 <context:component-scan base-package="com.simonefolinojavablog.persistence.dao">
  <context:include-filter type="annotation"
   expression="org.springframework.stereotype.Repository" />
 </context:component-scan>


 <!-- HSQLDB datasource -->
 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
  <property name="driverClass" value="org.hsqldb.jdbcDriver" />
  <property name="jdbcUrl" value="jdbc:hsqldb:mem:ex" />
  <property name="user" value="sa" />
  <property name="password" value="" />
 </bean>
  
 <bean id="entityManagerFactory" depends-on="schemaGenerator"
  class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="persistenceUnitName" value="persistenceUnitTest" />
  <property name="dataSource" ref="dataSource" />
  <property name="jpaVendorAdapter">
   <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
    <property name="showSql" value="true" />
    <property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect" />
    <property name="generateDdl" value="true" />
   </bean>
  </property>
 </bean>

 <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
  <property name="entityManagerFactory" ref="entityManagerFactory" />
  <property name="dataSource" ref="dataSource" />
 </bean>

 <bean id="schemaGenerator" class="com.simonefolinojavablog.persistence.test.SchemaGenerator">
  <property name="schema" value="ExampleSchema" />
  <property name="dataSource" ref="dataSource" />
 </bean>

</beans>
SchemaGenerator.java
package com.simonefolinojavablog.persistence.test;

import javax.sql.DataSource;


import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.core.JdbcTemplate;


public class SchemaGenerator implements InitializingBean {

    private String schema;
    private DataSource dataSource;

    public String getSchema() {
        return schema;
    }

    public void setSchema(String schema) {
        this.schema = schema;
    }

    public DataSource getDataSource() {
        return dataSource;
     }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

     public void afterPropertiesSet() throws Exception {
     JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        jdbcTemplate.execute("CREATE SCHEMA " + schema + " AUTHORIZATION DBA");
    }
}
Finally you have to declare the dependencies in your pom.xml in order to use the features described above. In this snippet i'll show the main dependencies you need. It's important to notice that hsqldb and c3p0 have test scope because they are only used for test aims.

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-tx</artifactId>
   <version>3.0.4.RELEASE</version>
   <scope>compile</scope> 
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-orm</artifactId>
   <version>3.0.4.RELEASE</version>
   <scope>compile</scope>
  </dependency>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-test</artifactId>
   <version>3.0.4.RELEASE</version>
   <scope>compile</scope>
  </dependency>
  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-entitymanager</artifactId>
   <version>3.5.5-Final</version>
  </dependency>
  <dependency>
   <groupId>org.hibernate.javax.persistence</groupId>
   <artifactId>hibernate-jpa-2.0-api</artifactId>
   <version>1.0.0.Final</version>
  </dependency>
  <dependency>
   <groupId>org.slf4j</groupId>
   <artifactId>slf4j-log4j12</artifactId>
   <version>1.5.8</version>
   <exclusions>
    <exclusion>
     <artifactId>slf4j-api</artifactId>
     <groupId>org.slf4j</groupId>
    </exclusion>
   </exclusions>
  </dependency>
  <dependency>
   <groupId>org.hsqldb</groupId>
   <artifactId>hsqldb</artifactId>
   <version>1.8.0.10</version>
   <scope>test</scope>
  </dependency>
  <dependency>
   <groupId>c3p0</groupId>
   <artifactId>c3p0</artifactId>
   <version>0.9.1.2</version>
   <scope>test</scope>
  </dependency>

What you'll see from the console is the following output:
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.simonefolinojavablog.persistence.test.ContactDAOTest
------------testSaveContact(): start -------
Hibernate: 
    insert 
    into
        ExampleSchema.Contacts
        (number, id) 
    values
        (?, ?)
------------testSaveContact(): end -------
------------testRetrieveContact(): start -------
Hibernate: 
    select
        contact0_.id as id0_0_,
        contact0_.number as number0_0_ 
    from
        ExampleSchema.Contacts contact0_ 
    where
        contact0_.id=?
Contact [id=1, number=22xxxx]
------------testRetrieveContact(): end -------
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.596 sec

Results :

Tests run: 2, Failures: 0, Errors: 0, Skipped: 0

No comments :

Post a Comment

Suggested Pages