Статьи

Простое интеграционное тестирование с Spring + Hibernate

 Я виновен в том, что до сих пор не писал интеграционное тестирование (по крайней мере, для транзакций, связанных с базой данных) Поэтому, чтобы искоренить вину, я прочитал о том, как можно достичь этого с минимальными усилиями в выходные дни. Придумал небольшой пример, показывающий, как этого легко добиться, используя Spring и Hibernate. С помощью интеграционного тестирования вы можете протестировать свой уровень DAO (объекта доступа к данным) без необходимости развертывания приложения. Для меня это огромный плюс, так как теперь я могу даже тестировать свои критерии, именованные запросы и сортировку без необходимости запускать приложение.

В hibernate есть свойство, позволяющее указать сценарий sql, запускаемый при инициализации фабрики сеансов. Теперь я могу заполнять таблицы данными, которые требуются для моего уровня DAO. Свойство выглядит следующим образом;

    <prop key="hibernate.hbm2ddl.import_files">import.sql</prop>  

Согласно документации по hibernate , у вас может быть много SQL-скриптов, разделенных запятыми. Один из моментов здесь заключается в том, что вы не можете создавать таблицы, используя скрипт. Потому что для запуска скрипта сначала необходимо создать схему. Даже если вы выполняете оператор create table внутри скрипта, он игнорируется при выполнении скрипта, как я его видел.

Позвольте мне сначала показать вам класс DAO, который я собираюсь проверить;

     package com.unittest.session.example1.dao;  
      
    import org.springframework.transaction.annotation.Propagation;  
    import org.springframework.transaction.annotation.Transactional;  
      
    import com.unittest.session.example1.domain.Employee;  
      
    @Transactional(propagation = Propagation.REQUIRED)  
    public interface EmployeeDAO {  
      
     public Long createEmployee(Employee emp);  
       
     public Employee getEmployeeById(Long id);  
    }  

    package com.unittest.session.example1.dao.hibernate;  
      
    import org.springframework.orm.hibernate3.support.HibernateDaoSupport;  
      
    import com.unittest.session.example1.dao.EmployeeDAO;  
    import com.unittest.session.example1.domain.Employee;  
      
    public class EmployeeHibernateDAOImpl extends HibernateDaoSupport implements  
      EmployeeDAO {  
      
     @Override  
     public Long createEmployee(Employee emp) {  
      getHibernateTemplate().persist(emp);  
      return emp.getEmpId();  
     }  
      
     public Employee getEmployeeById(Long id) {  
      return getHibernateTemplate().get(Employee.class, id);  
     }  
    }  

Ничего особенного, просто простой DAO с двумя методами, один из которых должен быть сохранен, а другой — получить. Для проверки метода поиска мне нужно заполнить таблицу Employee некоторыми данными. Это где сценарий импорта SQL, который был объяснен ранее, вступает в игру. Файл import.sql выглядит следующим образом;

    insert into Employee (empId,emp_name) values (1,'Emp test');  

Это просто основной скрипт, в котором я вставляю одну запись в таблицу сотрудников. Здесь еще раз обратите внимание, что для запуска сценария sql таблицу сотрудника необходимо создать с помощью опции DDL автоматического создания hibernate. Более подробную информацию можно найти здесь . Также сценарий import.sql в моем случае находится в classpath. Это необходимо для того, чтобы его можно было выполнить при создании фабрики сеансов.

Далее давайте посмотрим, насколько легко запустить интеграционные тесты с помощью Spring.

     package com.unittest.session.example1.dao.hibernate;  
      
    import static org.junit.Assert.*;  
      
    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 org.springframework.test.context.transaction.TransactionConfiguration;  
      
    import com.unittest.session.example1.dao.EmployeeDAO;  
    import com.unittest.session.example1.domain.Employee;  
      
    @RunWith(SpringJUnit4ClassRunner.class)  
    @ContextConfiguration(locations="classpath:spring-context.xml")  
    @TransactionConfiguration(defaultRollback=true,transactionManager="transactionManager")  
    public class EmployeeHibernateDAOImplTest {  
      
     @Autowired  
     private EmployeeDAO employeeDAO;  
       
     @Test  
     public void testGetEmployeeById() {  
      Employee emp = employeeDAO.getEmployeeById(1L);  
        
      assertNotNull(emp);  
     }  
       
     @Test  
     public void testCreateEmployee()  
     {  
      Employee emp = new Employee();  
      emp.setName("Emp123");  
      Long key = employeeDAO.createEmployee(emp);  
        
      assertEquals(2L, key.longValue());  
     }  
      
    }  

A few things to note here is that you need to instruct to run the test within a Spring context. We use the SpringJUnit4ClassRunner for this. Also the transction attribute is set to defaultRollback=true. Note that with MySQL, for this to work, your tables must have the InnoDB engine set as the MyISAM engine does not support transactions.

And finally i present the spring configuration which wires everything up;

     <?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:aop="http://www.springframework.org/schema/aop"  
     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-2.5.xsd    
              http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd    
              http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd    
              http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">  
      
      
     <context:component-scan base-package="com.unittest.session.example1" />  
     <context:annotation-config />  
      
     <tx:annotation-driven />  
      
     <bean id="sessionFactory"  
      class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">  
      <property name="packagesToScan">  
       <list>  
        <value>com.unittest.session.example1.**.*</value>  
       </list>  
      </property>  
      <property name="hibernateProperties">  
       <props>  
        <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>  
        <prop key="hibernate.connection.driver_class">com.mysql.jdbc.Driver</prop>  
        <prop key="hibernate.connection.url">jdbc:mysql://localhost:3306/hbmex1</prop>  
        <prop key="hibernate.connection.username">root</prop>  
        <prop key="hibernate.connection.password">password</prop>  
        <prop key="hibernate.show_sql">true</prop>  
        <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>  
        <!-- -->  
        <prop key="hibernate.hbm2ddl.auto">create</prop>  
        <prop key="hibernate.hbm2ddl.import_files">import.sql</prop>  
       </props>  
      </property>  
     </bean>  
      
     <bean id="empDAO"  
      class="com.unittest.session.example1.dao.hibernate.EmployeeHibernateDAOImpl">  
      <property name="sessionFactory" ref="sessionFactory" />  
     </bean>  
      
     <bean id="transactionManager"  
      class="org.springframework.orm.hibernate3.HibernateTransactionManager">  
      <property name="sessionFactory" ref="sessionFactory" />  
     </bean>  
      
    </beans>  

That is about it. Personally i would much rather use a more light weight in-memory database such as hsqldb in order to run my integration tests.

Here is the eclipse project for anyone who would like to run the program and try it out.