Я виновен в том, что до сих пор не писал интеграционное тестирование (по крайней мере, для транзакций, связанных с базой данных) Поэтому, чтобы искоренить вину, я прочитал о том, как можно достичь этого с минимальными усилиями в выходные дни. Придумал небольшой пример, показывающий, как этого легко добиться, используя 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.