单元测试:使用AbstractTransactionalJUnit4SpringContextTests遇到的有趣现象

由于各种原因,正在进行中的一个项目,并没有严格遵守测试先行的最佳实践。我们先写功能代码,然后再补充测试代码。

这是一个普通的Java WebApp项目,在服务器端使用的是SpringMVC 3 和 Hibernate 3.6, 按照最一般的层次划分,采用的是MVC + Service + Persistence 的做法。在开发过程中,对于Service这层,初始开发过程中,并没有立即采用面向接口的方式,拟考虑在Service功能代码相对稳定之后,通过重构来抽取接口。

于是起初原始Service层的代码只有 ****ServiceImpl 等诸如这样的类,例如:

......
......

@Service("trxDefService")
@Transactional(readOnly=true)
public class TrxDefServiceImpl {

private static final Logger logger = LoggerFactory.getLogger(TrxDefServiceImpl.class);

private GenericDao<TrxDef, Long> trxDefDao;

......
......

@Autowired
public void setDao(
......
......

@Resource(name="circularCheckingService")
public void setCircularCheckingService(CircularReferenceService circularService) {
this.circularService = circularService;
}

......
......


针对这个Service实现,相关的单元测试代码如下:

......
@ContextConfiguration(locations = {"classpath:/TrxDefServiceImplTest-context.xml"})
public class TrxDefServiceImplTest extends AbstractTransactionalJUnit4SpringContextTests {

private TrxDefServiceImpl trxDefService;

@Autowired
public void setTrxDefService(TrxDefServiceImpl trxDefService) {
this.trxDefService = trxDefService;
}

@Test
public void getAllListener() {
List<Listener> list = trxDefService.getAllListener();
assertNotNull(list);
assertTrue(list.size() >= 0);

}
......


在这里比较有意思的一个现象是,这个TestCase我从Eclipse IDE上运行的时候,测试能够通过;但是从Ant中运行同样的TestCase则会失败,提示报错如下:
"No matching bean of type [foo.bar.tm.trxdef.service.TrxDefServiceImpl] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency."


如果不采用@Autowired的byType形式,而是通过byName的方式注入Service:

private TrxDefServiceImpl trxDefService;

@Resource(name="trxDefService")
public void setTrxDefService(TrxDefServiceImpl trxDefService) {
this.trxDefService = trxDefService;
}

错误提示则更加直观:
"org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'trxDefService' must be of type [foo.bar.tm.trxdef.service.TrxDefServiceImpl], but was actually of type [$Proxy34]"

由此看来无论是byType注入还是byName注入,通过Ant启动的JUnit测试总是不通过,从错误消息来看,个人猜测是因为由于没有采用面向接口的方式编程,代码编译时指定的trxDefService类型[foo.bar.tm.trxdef.service.TrxDefServiceImpl] 和 运行时通过(c3p0或者cglib)动态生成的类型 [$Proxy34] 不一致,所以导致service注入失败。

但如果声明了Service接口,[foo.bar.tm.trxdef.service.TrxDefServiceImpl] 和动态代理类[$Proxy??] 都是Service接口的实现,这样的情况下可以正常注入service,测试可以正常运行。

解决上述问题的方式比较简单,从TrxDefServiceImpl上抽取出接口,单元测试代码中的Service声明为接口即可。(我参考了[url]http://zhongzhihua.iteye.com/blog/613305[/url],很给力)


......
@Service("trxDefService")
@Transactional(readOnly=true)
public class TrxDefServiceImpl implements TrxDefService {
.......




......
@ContextConfiguration(locations = {"classpath:/TrxDefServiceImplTest-context.xml"})
public class TrxDefServiceImplTest extends AbstractTransactionalJUnit4SpringContextTests {

private TrxDefService trxDefService;

@Autowired
public void setTrxDefService(TrxDefService trxDefService) {
this.trxDefService = trxDefService;
}
......



另外我感到好奇的,就是如果不采用接口编程的方式,直接在Eclipse使用JUnit操作运行测试时可以通过的,而通过Ant运行同样的代码则会报错。我初步的猜测是Eclipse在编译的时候,自动帮我做了某些转换。

另外一种可能就是Eclipse在编译工作之外,做了某些手脚:我通过重构,从TrxDefServiceImpl抽取TrxDefService接口的时候,单元测试代码中的原来声明为TrxDefServiceImpl类型的变量trxDefService,被重构为TrxDefService接口类型了,看上去是比较诡异的,至于重构动作时如何关联到单元测试代码TrxDefServiceImplTest,似乎又与@ContextConfiguration(locations = {"classpath:/TrxDefServiceImplTest-context.xml"}) 有关系。

在[url]http://stackoverflow.com/questions/4805794/failed-to-load-applicationcontext-nosuchbeandefinitionexception-when-running-jun[/url]提到的另外一个可能的原因,就是Ant运行JUnit测试的时候,类路径上找不到c3p0或者cglib,从而导致运行失败。

限于个人的才智,对于上述现象的基础原因,暂时只能停留在 知其然不知其所以然 的水平上,作为学习笔记先记录下来;也欢迎这方面有研究的朋友提供好的想法。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值