Implementing Junit in Spring and Hibernate Apps

Enterprise java applications are divided into three main layers ViewLayer, BusinessLayer and DAO layer. When a DAO layer is developed we need to take care of unit testing of the same. Junit isĀ  one of the best ways to unit test the applications. In this blog, I am demonstrating how to implement JUnit test class when Hibernate controls the persistence and Spring controls the application.

I have tried to use the best possible pattern for DAO layer designing. Common methods have been abstracted in a base class. I have also used of annotations to make code cleaner and readable and AbstractEntity for generic operations. This example also demonstrates how to deal with lazy loading in unit test execution.

Prerequisites:

1) I am assuming you are familiar with basics of Spring and Hibernate (Sping ver: 3.1.1.RELEASE, Hibernate Ver: 4.1.1.Final)

2) Eclipse Indigo with Maven 3

3) Knowledge of JUnit

Here are the steps to develop a working exmple:

1) Create Entity classes.

AbstractEntity.java


@MappedSuperclass
public abstract class AbstractEntity implements Serializable {

private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
protected Long id;

@Version
@Column(name = "version")
private Integer version;

public Long getId() {
return id;
}

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

public Integer getVersion() {
return version;
}
}

Employee.java

@Entity
public class Employee extends AbstractEntity{

/*@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;*/

private String name;

@OneToMany(mappedBy = "employee", cascade=CascadeType.ALL, fetch=FetchType.LAZY)
private List<Address> addresses = new ArrayList<Address>();

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public List<Address> getAddresses() {
return addresses;
}

public void setAddresses(List<Address> addresses) {
this.addresses = addresses;
}

/*public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}*/
}

Address.java


@Entity
public class Address extends AbstractEntity{

@Basic
private String addressLine1;

@Basic
private Long employeeId;

@ManyToOne
@JoinColumn(name="id", insertable=false, updatable=false)
private Employee employee;

public Address(){}

public Address(Employee employee) {
this.employee = employee;
//employeeId = employee.getId();
addressLine1 = "addressline1";
}

public Employee getEmployee() {
return employee;
}

public void setEmployee(Employee employee) {
this.employee = employee;
}

public String getAddressLine1() {
return addressLine1;
}

public void setAddressLine1(String addressLine1) {
this.addressLine1 = addressLine1;
}

public Long getEmployeeId() {
return employeeId;
}

public void setEmployeeId(Long employeeId) {
this.employeeId = employeeId;
}

}

2) Develop DAO classes

AbstractDao.java


/**
*
* @author ajaidka
*
*/
@Scope("prototype")
public class AbstractDao<T extends AbstractEntity> {
private SessionFactory sessionFactory;

@Autowired
private void setSessionFactory(SessionFactory factory) {
this.sessionFactory = factory;

}

public Session getSession(){
return sessionFactory.getCurrentSession();
}

private final Class<T> type;

public Class<T> getType() {
return type;
}

public AbstractDao(Class<T> type) {
this.type = type;
}

@SuppressWarnings("unchecked")
public T findById(Long id) {
return (T) getSession().get(type, id);
}

@SuppressWarnings("unchecked")
public List<T> findAll() {
return getSession().createCriteria(type)
.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).list();
// return session().createQuery("from " + getType().getName()).list();
}

public void delete(Long id) {
getSession().delete(findById(id));
}

public void delete(T entity) {
getSession().delete(entity);
}

public void save(T entity) {
getSession().saveOrUpdate(entity);
}

public void merge(T entity) {
getSession().merge(entity);
}

public void update(T entity) {
getSession().update(entity);
}

public void attachToSession(T entity) {
getSession().lock(entity, LockMode.NONE);
}

}

EmployeeDao.java


public interface EmployeeDao {
public Employee getEmployeeByName(String name);
public void save(Employee employee);
}

EmployeeDaoImpl.java


@Scope("prototype")
@Repository(value="employeeDao")
public class EmployeeDaoImpl extends AbstractDao<Employee> implements EmployeeDao{

public EmployeeDaoImpl() {
super(Employee.class);
}

public void deleteEmployee(Employee employee) {
super.delete(employee);
}

@Override
public Employee getEmployeeByName(String name) {
return (Employee) getSession().createCriteria(getType())
.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY).add(Restrictions.eq("name", name)).uniqueResult();
}

@Override
public void save(Employee employee) {
getSession().saveOrUpdate(employee);
}
}

3) Write Spring context “testApplicationContext.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:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="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/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

<context:component-scan
base-package="net.paxcel"/>

<!-- We tell Spring that we are using annotations -->
<context:annotation-config/>

<tx:annotation-driven/>

<bean
p:location="jdbc.properties"/>

<bean id="dataSource"

p:driverClassName="${jdbc.driverClassName}"
p:url="${jdbc.url}"
p:username="${jdbc.username}"
p:password="${jdbc.password}"
/>

<!-- Database Property -->
<bean id="hibernateProperties">
<property name="properties">
<props>
<!-- debug/loggin props -->
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.formate_sql">false</prop>
<prop key="hibernate.user_sql_comments">false</prop>
<!-- real properties -->
<!-- <prop key="hibernate.current_session_context_class">thread</prop> -->
<prop key="hibernate.hbm2ddl.auto">create-drop</prop>
<!-- <prop key="hibernate.connection.autocommit">true</prop> -->
<prop key="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</prop>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.cache.provider_class">org.hibernate.cache.NoCacheProvider</prop>
<!-- <prop key="hibernate.current_session_context_class">org.hibernate.context.ThreadLocalSessionContext</prop> -->
<!-- <prop key="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</prop> -->
</props>
</property>
</bean>

<bean id="transactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>

<bean id="namingStrategy"/>

<!-- Hibernate SessionFactory -->
<bean id="sessionFactory"
name="hsqlSessionFactory"

p:configLocation="classpath:hibernate.cfg.xml"
p:hibernateProperties-ref="hibernateProperties"
p:dataSource-ref="dataSource"
p:namingStrategy-ref="namingStrategy">

<property name="annotatedClasses">
<list>
<value>net.paxcel.hibernate.pojos.Employee</value>
<value>net.paxcel.hibernate.pojos.Address</value>
</list>
</property>

</bean>

</beans>

4) Hibernate config xml

hibernate.cfg.xml


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>

<mapping class="net.paxcel.hibernate.pojos.Employee"/>
<mapping class="net.paxcel.hibernate.pojos.Address"/>
</session-factory>
</hibernate-configuration>

5) Write jdbc.properties, log4j.properties and import.sql as required.

Import.sql on classpath, helps to populate database before beginning testcases. Pl. see it in attached demo.

6) Write Junit classes


@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:testApplicationContext.xml"})

public class BaseTest {

@Ignore
@Test
public void testDummy() {
Assert.assertNull(null);
}

@Autowired
private SessionFactory sessionFactory;

@Before
public void setUp() throws Exception {
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(sessionFactory.openSession()));
}

@After
public void tearDown() throws Exception {
SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
SessionFactoryUtils.closeSession(sessionHolder.getSession());
}

}

EmployeeDaoTest


public class EmployeeDaoTest extends BaseTest{

@Resource
private EmployeeDaoImpl employeeDao;

@Test
public void testFindAll(){
Assert.assertFalse(employeeDao.findAll().isEmpty());
}

@Test
public void testFindById(){
Assert.assertNotNull(employeeDao.getEmployeeByName("Badal"));
}

@Test
public void testSaveEmployee(){
Employee employee = new Employee();
employee.setName("Flash gordan");

List<Address> addresses = new ArrayList<Address>();
addresses.add(new Address(employee));

employee.setAddresses(addresses);
employeeDao.save(employee);
Assert.assertNotNull(employeeDao.findById(employee.getId()));
Assert.assertTrue(employeeDao.findById(employee.getId()).getAddresses().size() > 0);
}

@Test
public void testEmployeeDelete(){
Employee employee = employeeDao.getEmployeeByName("Badal");
long id = employee.getId();
employeeDao.delete(id);
Assert.assertNull(employeeDao.findById(id));
}

}

7) All set to run a test case.

Download example code here http://paxcel.net/blog/wp-content/uploads/2012/09/SpingHibernateJunit.rar

References:

http://static.springsource.org/spring/docs/2.5.x/reference/orm.html

http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/collections.html

http://livingtao.blogspot.in/2007/08/unit-testing-with-junit-spring-and.html

Thanks

Aashu Jaidka

Leave a Reply

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


six − = 4

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>