在多数据源(JTA)环境下使用Spring TestContext Framework
项目背景
当时没有做单元测试、自动化集成测试,但是有人工的验收测试,关键业务有压力测试。也许有人会侧目,但是这是公司与项目的现状……期间曾经有领导想推行单元测试,但是未推行成功,就其原因,我觉得有如下几条:
随着公司业务的飞速发展,系统使用的场景越来越复杂,对以用功能的升级改造也越来越频繁,代码重构越来越多,所以测试人员的压力很大,由功能升级或重构导致的bug也多了起来。此时通过自动化的测试脚本来提高开发质量与系统稳定性就越来越重要了。
开始集成测试?为什么选择Spring TestContext Framework与集成测试首先,项目本来就基于Spring2.5容器,这是个天然的因素。其次,由于系统功能绝大多数都是与数据库的交互,所以测试数据库操作正确与否,是一个主要的目的。最后,由项目背景可知,数据库的事务情况比较复杂,在测试时需要完善的事务环境支持。所以根据以上几点,我们选择基于Spring TestContext Framework做集成测试。
Spring TestContext Framework集成测试环境的构建?构建数据源与JtaTransactionManager根据系统的事务特点,我们需要使用脱离应用服务器的JNDI数据源,而不能使用轻量级的数据源(如DBCP),因为JtaTransactionManager是不支持的。通过查阅Spring的文档,我们使用Spring的JotmFactoryBean结合ow2-jotm的分发版本中的StandardXADataSource来构建脱离应用服务器的JNDI数据源,配置如下:
?以上的配置,可以参考Spring JotmFactoryBean的doc,里面有比较详尽的说明,这里有两点说明:
doc中只使用了一个数据源,因为其只是一个示例,而我们在测试环境中需要多个数据源。事务配置使用AOP做横切,在集成测试环境中,其配置可以与生产环境相同,这样也保证了测试的真实性与有效性。在测试环境中需要StandardXAPoolDataSource的配置,否则当使用AbstractTransactionalJUnit4SpringContextTests中的SimpleJdbcTemplate执行修改数据的sql时,在JTA事务环境下,事务会失效,数据会直接提交到数据库中。
测试基类的选择与扩展Spring TestContext Framework支持JUnit3.8、JUnit4和TestNG,这里我们以JUnit4为例。首先让我们来看一下Spring基于JUnit4为我们提供的基类:AbstractTransactionalJUnit4SpringContextTests、AbstractJUnit4SpringContextTests。通过查阅文档与代码,我们知道,AbstractTransactionalJUnit4SpringContextTests为其子类提供了事务能力以及简单的数据初始化接口,而其本身就是AbstractJUnit4SpringContextTests的子类。在大多数应用场景中,直接继承AbstractTransactionalJUnit4SpringContextTests,从而获得事务能力是一个不错的选择。
Transaction 与 Flush由于项目中使用了Hibernate,默认的FlushModel是"manual"或"Never",所以对于Inert语句的Flush会在事务提交的时候执行。由于测试代码中拥有@Transactional以及事务的传播策略,所以当Service中的事务增强方法执行结束之后,事务并未像在生产环境中那样提交,则Hibernate也没有flush。导致我们想在一个测试单元中验证事务提交后的结果变的不再可行。
基于此,我们有必要设置Hibernate,希望它立即提交,或者我们手动提交,以保证Session的Flush。为了不修改我们的代码以及尽可能的模拟代码在生产环境中的执行,我们通过AOP做一个后置处理,即在每次业务Service执行完,将SessionFluash:
)。而测试会促使我们去编写更健壮的代码,良好的测试脚本,让我们有重构的勇气,增强程序员的信心,这种影响无疑是无形和重要的。?
?
再看看多数据源事务实用方法:package com.frameworkset.common;import javax.transaction.RollbackException;import org.junit.Test;import com.frameworkset.common.poolman.DBUtil;import com.frameworkset.orm.transaction.TransactionManager;public class TestMutiDBTX {public static void testMutiDBTX(){TransactionManager tm = new TransactionManager();try{tm.begin();DBUtil db = new DBUtil();db.executeDelete("bspf","delete from table1 where id=1");db.executeUpdate("query","update table1 set value='test' where id=1");tm.commit();DBUtil.debugStatus();}catch(Exception e){try {tm.rollback();} catch (RollbackException e1) {e1.printStackTrace();}}}@Testpublic void testMutiDBButSampleDatabaseTX(){TransactionManager tm = new TransactionManager();try{tm.begin();DBUtil db = new DBUtil();db.executeDelete("bspf","delete from table1 where id=1");db.executeUpdate("mq","update table1 set value='test' where id=1");tm.commit();DBUtil.debugStatus();}catch(Exception e){try {tm.rollback();} catch (RollbackException e1) {e1.printStackTrace();}}DBUtil.debugStatus();}}
bbossgroups-2.0-RC1下载地址:
http://sourceforge.net/projects/bboss/files/bbossgroups-2.0-RC1/bbossgroups-2.0-RC1.zip/download
3 楼 hellohank 2010-08-11 对于业务层来说,并不赞成使用单元测试,而应该使用集成测试。如果做单元测试,从工作量上来说,有得不偿失的感觉~
顶楼主啊~ 4 楼 former 2010-08-11 <div class="quote_title">leifeng2 写道</div>
<div class="quote_div">我想一个应用程序最好应该以一个数据源为准,如果需要的数据来源于多个数据库,可以考虑通过建立如数据库链路的方式,实现库于库之间的互访。</div>
<p>?</p>
<p>?不知道你说的数据库链路的方式是如何处理的,可以贴出一些例子和代码看看吗?在项目中也有业务是通过Oralce桥接远程MySQL处理的业务,但却是为了某些业务上的目的而实现的。</p>
<p>基于JTA的事务处理,有其自身的适用场景,如其可以保证在多库之间事务的原子性,兼容已有的数据库等。不知道你说的数据库链路方式对于不同数据库产品、不同物理位置分布是否可以很好的支持?对于Java代码中使用Hibernate的Schema设置是否可以很好的支持?DBA的管理难度如何?</p>