`

单元测试:使用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声明为接口即可。(我参考了http://zhongzhihua.iteye.com/blog/613305,很给力)

......
@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"}) 有关系。

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

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


1
5
分享到:
评论

相关推荐

    spring AbstractTransactionalJUnit4SpringContextTests,jar包

    spring testContext jar包下载,3.0

    junit-test-dao

    - **测试类**:定义一个测试类`UserDAOTest`,该类继承自`AbstractTransactionalJUnit4SpringContextTests`或使用`@RunWith(SpringRunner.class)`和`@Transactional`注解来管理事务。 - **测试方法**:编写具体的...

    Spring+hibernate 单元测试

    2. **定义测试类**:测试类通常需要继承自 `AbstractTransactionalJUnit4SpringContextTests` 或使用 `@RunWith(SpringRunner.class)` 来集成 Spring 测试框架。 3. **配置 TestExecutionListeners**:可以通过 `@...

    主题:在Spring中结合Dbunit对Dao进行集成单元测试

    在Spring项目中,我们需要创建一个测试配置类,该类通常继承自`AbstractTransactionalDataSourceSpringContextTests`或`AbstractTransactionalJUnit4SpringContextTests`,这两个类提供了事务管理和数据源的自动...

    spring与dbunit集成测试

    1. **配置Spring测试环境**:使用Spring Test模块,创建一个继承自`AbstractTransactionalDataSourceSpringContextTests`或`AbstractTransactionalJUnit4SpringContextTests`的测试类。这两个类提供了事务管理,确保...

    dbunit-2.1.zip_dbunit docs_zip

    import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests; @ContextConfiguration(locations = { "classpath:spring-context.xml" }) public class MyDatabaseTest extends...

    022-preload-database-execute-sql-spring-testing

    Spring还提供了如`AbstractTransactionalDataSourceSpringContextTests`和`AbstractTransactionalJUnit4SpringContextTests`这样的基类,它们提供了自动事务管理和数据库清理的功能,简化了数据库测试的编写。...

    Hibernate用UUID作为主键的Demo

    import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests; @ContextConfiguration(locations = {"classpath:spring-context.xml"}) public class DemoEntityTest extends ...

    spring-hibernate-criteria-builder-p6spy

    Simple Spring(Core,JPA,ORM,MVC)5个JPA 2 Hibernate 5项目,具有: ...Spring测试:SpringJUnit4ClassRunner + AbstractTransactionalJUnit4SpringContextTests JUnit的 哈姆克雷斯特 莫基托

    dbunit-spring-demo:DBUnit Utility演示(使用Spring)

    3. **测试类**:在`src/test/java`目录下,会有测试类,这些类通常继承自Spring的`AbstractTransactionalJUnit4SpringContextTests`或类似的测试基类。测试类会使用DBUnit的`@Before`和`@After`注解来在每个测试开始...

Global site tag (gtag.js) - Google Analytics