单元测试最佳实践:TestNG与Spring的配置与整合
如果我们用Spring管理程序中的Bean,那么关于单元测试的几个问题需要解决:
- 怎样启动单元测试前加载Spring的context
- 怎样允许单元测试代码使用mock来取代Spring中配置的bean,比如数据库连接
- 测试中怎样维护测试数据的状态
下面简单介绍这三个问题的解决方法。
对第一个问题,首先我们都很清楚在web程序中,运行时的Spring启动可以通过在web.xml里面配置servlet和listener来解决。这个就不另外说明了。那么解决问题的办法分为以下几个:
1. 为测试建立单独的Spring的xml配置文件。这样就允许你为测试单独设置程序运行的依赖。有些对外界环境的依赖,比如JNDI,Data Source,都可以用mock对象来取代。为了生成mock对象,可以写一个工厂类,然后在Spring中使用这个工厂类来生成mock对象。SessionFactory和HibernateTemplate都可以用这种方式mock。
2. 建立一个测试基类,为其加上注解来启动Spring上下文。所有的单元测试可以继承这个类,那么它们在运行时,Spring就会自动加载,测试中需要的依赖也会被加载。TestNG的代码如下:
这种方式使用JUnit4其实会更方便一些,不过TestNG也没太大的问题。
3. 设置内存数据库供测试中使用。目的是方便、高效。这里举HSQLDB为例,在Spring中配置一下就会自己启动:
4. 使用DBUnit维护测试数据。把清空数据库和注入测试数据的代码写在@BeforeTest里(或者如果必要的话也可以在每个测试用例前做,比如@BeforeClass,但如果数据大的话会对性能有影响),这样每次测试前都可以保证数据库是干净的。也可以把维护数据的代码放在一个基类里,比如:
转自: http://blog.csdn.net/blackchoc/article/details/5711860
项目中用到了testNG作为单元测试工具,至于testNG对比junit有啥好处不太清楚,至少从程序员写testcase来说似乎和junit 4.x并没有太大的区别。但是据说和一些测试工具整合的时候比较方便。ok,这不是重点。
Spring专门为Junit testNG提供了一套测试集成接口类——AbstractSpringContextTests类,对于testNG就是其子类:AbstractTestNGSpringContextTests。Spring和testNG整合后,进行单元测试的时只要test类继承该类,就可以方便的使用spring注入。实现了spring和testNG的无缝整合,我们可以像写普通类那样测试被spring IoC容器所管理的类(否则我们必须在开始执行单元测试前重新加载Spring beanfactory,再用getBean("xxx")的方式获取IoC容器中类。)
除此以外,对测试类spring beanfactory缓存,使得多个测试类之间可以共享同一个的beanfactory实例,从而减少了重复生成beanfactory,提高了运行效率。
继承该类的测试用例在spring管理的事务中进行,测试完后对数据库的记录不会造成任何影响。你对数据库进行一些操作后,它会自动把数据库回滚,这样就保证了你的测试对于环境没有任何影响
集成代码如下
- @ContextConfiguration
- (locations={"applicationContext.xml"})
- public class TestUser extends AbstractTestNGSpringContextTests{
- @Autowired
- UserService userService;
- @Test
- public void test_save_user(){
- User user = new User();
- user .setPassword("123456");
- user .setSex(1);
- user .setPartyName("test1");
- user .setEmail("aa@bb.com");
- userService.save(user);
- }
- @Test
- public void test_inject_factory(){
- Assert.assertNotNull(userService);
- }
- }
其中最重要的就是@ContextConfiguration。默认的从classpath目录下读取applicationContext.xml作为spring的启动配置文件(对应ClassPathXmlApplicationContext?)。
等同于(locations={"classpath:applicationContext1.xml"})。因此必须确保spring的配置文件在classpath中。
如果有多个spring配置,用逗号进行分隔(locations={"applicationContext.xml", "/applicationContex_transaction.xml"})
另外一种采用filepath定位spring配置文件(对应FileSystemXmlApplicationContext?).
比如在web项目中将相应的配置文件放到WEB-INF目录下"file:WebRoot/WEB-INF/config/applicationContext.xml",或者可以指定绝对路径。
实际问题:
项目中,将配置放在了web-inf/config/目录下对应的xml文件中,但是又在xml文件中对web-inf/config/properties/目录下的几个包括log4j.properties在内的几个properties文件进行了引用。
如果使用file方式指定spring配置文件位置的话,当加载到xml文件中引用的property文件时就会报文件找不到。因为仅仅指定采用file方式读取xml文件,对于对于property文件仍然会去classpath搜索。
解决方法
将Webroot加入到项目的classpath目录中。
或者更好的做法是指定在运行testNG测试时,将webroot加入到classpath中。
Eclipse中 run/run configuration菜单中进行配置