为什么需要回滚测试?
在开发过程中进行单元测试的目的是为了让系统的更稳定。在实施测试驱动开发的时候发现一个问题,有些用例对数据有特定要求。因为测试过程本身会引起数据的变化,所以导致测试用例无法反复测试。
譬如添加一条主键id为10的记录:
测试用例可能是这样的 :
boolean handle= deptService.addDept(“10","测试部门10");
Assert.assertEquals(true,handle);
这个测试用例可以跑通,但是无法反复测试,因为第二次测试的时候就因为主键不能重复而报错。
解决方法:
我们可以通过手动还原测试用例修改的内容。在刚刚的例子中可以在每次测试完成后删除那条记录。有的人已经发现这个好像也太麻烦了吧,数据简单还好,如果是一个比较复杂的操作对多张数据表进行修改,那些个测试用例也太累了吧。所以还有一个可选方案就是使用回滚测试,让测试用例执行完之后并不对数据库进行修改,这样也能实现用例反复测试的效果,而且无需添加手动代码。
spring3如何实现回滚测试?
前置条件
1 spring3环境配置成功2 可正常跑通junit测试
实现步骤:
1.继承spring事务测试类
继承AbstractTransactionalJUnit4SpringContextTests类(通常spring3的单元测试类AbstractJUnit4SpringContextTests)
2. 添加事务注解@TransactionConfiguration:
2.1在测试类的上方进行注解 @TransactionConfiguration(transactionManager="transactionManager",defaultRollback=false)2.2在需要回滚的方法上添加回滚标示 @Rollback(value=true)
3.在applicationContext.xml配置文件中添加 dataSource配置
如果不适用回滚测试的话 dataSource是可以不进行配置的,但是这里必须进行配置。(这个dataSource配置的是挺奇怪的,因为在sessionFactory配置项中已经指定hibernate.cfg.xml替代数据源配置了,不过不写这个dataSource是会报错的)
代码参考:
1 applicationContext.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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-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/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation"
value="classpath:hibernate.cfg.xml">
</property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/yusr?useUnicode=true&characterEncoding=UTF-8</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value>root</value>
</property>
</bean>
<!-- 配置事务管理 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<!-- 配置事务的传播性 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="remove*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="get*" propagation="REQUIRED"/>
<tx:method name="find*" propagation="REQUIRED"/>
<tx:method name="turn*" propagation="REQUIRED"/>
<tx:method name="del*" propagation="REQUIRED"/>
<tx:method name="move*" propagation="REQUIRED"/>
<tx:method name="edit*" propagation="REQUIRED"/>
<tx:method name="test*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 配置事务管理的哪些方法 -->
<aop:config>
<aop:pointcut id="allServiceMethod" expression="execution(* com.metecyu.*.dao.*.*(..)) or execution(* com.metecyu.*.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="allServiceMethod"/>
</aop:config>
<!-- yzp:true:表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理(不过这个项目不加就会报错,其它项目都不需要,我想可能是包版本的关系)-->
<aop:aspectj-autoproxy proxy-target-class="true" />
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor" />
<context:component-scan base-package="com.metecyu.yusr.dao" />
<context:component-scan base-package="com.metecyu.yusr.service" />
<!--
<context:component-scan base-package="com.megait.cw.task.service" />
<context:component-scan base-package="com.megait.xmgl.comm.service" /> -->
</beans>
2 测试用例
package com.metecyu.yusr.dao;
import java.util.List;
import javax.annotation.Resource;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;
import org.apache.log4j.Logger;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import com.metecyu.yusr.model.Dept;
import com.metecyu.yusr.service.DeptService;
@TransactionConfiguration(transactionManager="transactionManager",defaultRollback=false)
@ContextConfiguration(locations={"/applicationContext.xml","/hibernate.cfg.xml"})
public class DeptDAOTest extends AbstractTransactionalJUnit4SpringContextTests {
private static final Logger log = Logger.getLogger(DeptDAOTest.class);
@Resource
private DeptDAO deptDAO;
@Resource
DeptService deptService;
@Test
@Rollback(value=true)
public void testGetNewId(){
String deptid = "testDept";
String newDeptid = deptDAO.getNewDeptid(deptid);
Assert.assertEquals("和原id相同",newDeptid, deptid);
deptService.addDept(deptid,"测试部门","测试部门","1");
newDeptid = deptDAO.getNewDeptid(deptid);
Assert.assertEquals("第一次id重复,编号为01",newDeptid, deptid+"@01");
deptService.addDept(deptid+"@01","测试部门@01","","1");
deptService.addDept(deptid+"@02","测试部门@02","","1");
deptService.addDept(deptid+"@03","测试部门@03","","1");
newDeptid = deptDAO.getNewDeptid(deptid);
Assert.assertEquals("多条id重复,编号为当前末尾编号加1",newDeptid, deptid+"@04");
deptService.addDept(deptid+"@20","测试部门@20","","1");
newDeptid = deptDAO.getNewDeptid(deptid);
Assert.assertEquals("多条id重复,编号为当前末尾编号加1",newDeptid, deptid+"@21");
deptService.addDept(deptid+"@99","测试部门@99","","1");
newDeptid = deptDAO.getNewDeptid(deptid);
Assert.assertNotSame("第一次id重复,编号为01",newDeptid, deptid+"@00");
}
@Test
@Rollback(value=true)
public void testGenarateDeptId() throws BadHanyuPinyinOutputFormatCombination{
String deptname = "测试部门";
String newDeptid = deptDAO.genarateDeptId(deptname);
Assert.assertEquals("和原id相同",newDeptid, "csbm");
deptService.addDept("csbm","测试部门","测试部门","1");
newDeptid = deptDAO.genarateDeptId(deptname);
Assert.assertEquals("第一次id重复,编号为01",newDeptid, "csbm@01");
}
}