集成Spring
Unitils 提供了一些在Spring 框架下进行单元测试的特性。Spring 的一个基本特性就是,类要设计成为没有Spring 容器或者在其他容器下仍然易于进行单元测试。但是很多时候在Spring 容器下进行测试还是非常有用的。
Unitils 提供了以下支持 Spring 的特性:
ApplicationContext 配置的管理;
在单元测试代码中注入Spring 的Beans;
使用定义在Spring 配置文件里的Hibernate SessionFactory;
引用在Spring 配置中Unitils 数据源。
ApplicationContext配置
可以简单地在一个类、方法或者属性上加上@SpringApplicationContext 注解,并用Spring的配置文件作为参数,来加载Spring应用程序上下文。下面我们通过实例来介绍一下如何创建ApplicationContext。
代码清单16 14 加载Spring上下文
import org.junit.Test; import org.springframework.context.ApplicationContext; import org.unitils.UnitilsJUnit4; import org.unitils.spring.annotation.SpringApplicationContext; import org.unitils.spring.annotation.SpringBean; import com.baobaotao.service.UserService; import static org.junit.Assert.*; //①用户服务测试 public class UserServiceTest extends UnitilsJUnit4 { //①-1 加载Spring配置文件 @SpringApplicationContext({"baobaotao-service.xml", "baobaotao-dao.xml"}) private ApplicationContext applicationContext; //①-1 加载Spring容器中的Bean @SpringBean("userService") private UserService userService; //①-3 测试Spring容器中的用户服务Bean @Test public void testUserService (){ assertNotNull(applicationContext); assertNotNull(userService.findUserByUserName("tom")); } }
在①-1处,通过@SpringApplicationContext 注解加载baobaotao-service.xml和baobaotaodao.xml两个配置文件,生成一个Spring应用上下文,我们就可以在注解的范围内引用applicationContext这个上下文。在①-2处,通过@SpringBean注解注入当前Spring容器中相应的Bean,如实例中加载ID为"userService"的Bean到当前测试范围。在①-3处,通过JUnit断言验证是否成功加载applicationContext和userService。Unitils加载Spring上下文的过程是:首先扫描父类的@SpringApplicationContext注解,如果找到了就在加载子类的配置文件之前加载父类的配置文件,这样就可以让子类重写配置文件和加载特定配置文件。
细心的读者可能会发现,采用这种方式加载Spring应用上下文,每次执行测试时,都会重复加载Spring应用上下文。Unitils为我们提供在类上加载Spring应用上下文的能力,以避免重复加载的问题。
代码清单16 15 通过基类加载ApplicationContext
@SpringApplicationContext({"baobaotao-service.xml", "baobaotao-dao.xml"}) public class BaseServiceTest extends UnitilsJUnit4 { //加载Spring上下文 @SpringApplicationContext public ApplicationContext applicationContext; }
集成Hibernate
Hibernate是一个优秀的O / R开源框架,它极大地简化了应用程序的数据访问层开发。虽然我们在使用一个优秀的O/R框架,但并不意味我们无须对数据访问层进行单元测试。单元测试仍然非常重要。它不仅可以确保Hibernate映射类的映射正确性,也可以很便捷地测试HQL查询等语句。Unitils为方便测试 Hibernate,提供了许多实用的工具类,如HibernateUnitils就是其中一个,使用assertMappingWithDatabaseConsistent()方法,就可以方便测试映射文件的正确性。
SessionFactory 配置
可以简单地在一个类、方法或者属性上加上@ HibernateSessionFactory 注解,并用Hibernate的配置文件作为参数,来加载Hibernate上下文。下面我们通过实例来介绍一下如何创建SessionFactory。
代码清单16 17 通过基类加载SessionFactory
@HibernateSessionFactory("hibernate.cfg.xml") public class BaseDaoTest extends UnitilsJUnit4 { @HibernateSessionFactory public SessionFactory sessionFactory; @Test public void testSessionFactory(){ assertNotNull(sessionFactory); } }
在父类BaseDaoTest里指定了Hibernate配置文件,Hibernate应用上下文只会创建一次,然后在子类SimpleUserDaoTest里会重用这个应用程序上下文。加载Hibernate应用上下文是一个非常繁重的操作,如果重用这个Hibernate应用上下文就会大大提升测试的性能。
代码清单16 18 通过继承使用父类的SessionFactory
public class SimpleUserDaoTest extends BaseDaoTest { private UserDao userDao; //① 初始化UserDao @Before public void init(){ userDao = new WithoutSpringUserDaoImpl(); userDao.setSessionFactory(sessionFactory); //使用父类的SessionFactory } //② Hibernate映射测试 @Test public void testMappingToDatabase() { HibernateUnitils.assertMappingWithDatabaseConsistent(); } //③ 测试UserDao @Test public void testUserDao(){ assertNotNull(userDao); assertNotNull(userDao.findUserByUserName("tom")); assertEquals("tom", userDao.findUserByUserName("tom").getUserName()); } }
为了更好演示如何应用Unitils测试基于Hibernate数据访问层,在这个实例中不使用Spring框架。所以在执行测试时,需要先创建相应的数据访问层实例,如实例中的userDao。其创建过程如①处所示,先手工实例化一个UserDao,然后获取父类中创建的SessionFactory,并设置到UserDao中。在②处,使用Unitils提供的工具类HibernateUnitils中的方法测试我们的Hibernate映射文件。在③处,通过JUnit的断言验证 UserDao相关方法,看是否与我们预期的结果一致。
集成Dbunit
Dbunit是一个基于JUnit扩展的数据库测试框架。它提供了大量的类,对数据库相关的操作进行了抽象和封装。Dbunit通过使用用户自定义的数据集以及相关操作使数据库处于一种可知的状态,从而使得测试自动化、可重复和相对独立。虽然不用Dbunit也可以达到这种目的,但是我们必须为此付出代价(编写大量代码、测试及维护)。既然有了这么优秀的开源框架,我们又何必再造轮子。目前其最新的版本是2.4.8。
随着Unitils的出现,将Spring、Hibernate、DbUnit等整合在一起,使得DAO层的单元测试变得非常容易。Unitils采用模块化方式来整合第三方框架,通过实现扩展模块接口org.unitils.core.Module来实现扩展功能。在Unitils中已经实现一个DbUnitModule,很好整合了DbUnit。通过这个扩展模块,就可以在Unitils中使用Dbunit强大的数据集功能,如用于准备数据的@DataSet注解、用于验证数据的@ExpectedDataSet注解。
自定义扩展模块
Unitils通过模块化的方式来组织各个功能模块,对外提供一个统一的扩展模块接口org.unitils.core.Module来实现与第三方框架的集成及自定义扩展。在Unitils中已经实现目前一些主流框架的模块扩展,如Spring、Hibernate、DbUnit、Testng等。如果这些内置的扩展模块无法满足需求,我们可以实现自己的一些扩展模块。扩展Unitils模块很简单,如代码清单16-19所示。
代码清单16 19 CustomExtModule
package sample.unitils.module; import java.lang.reflect.Method; import org.unitils.core.TestListener; import org.unitils.core. Module; //① 实现Module接口 public class CustomExtModule implements Module { //② 实现获取测试监听的方法 public TestListener getTestListener() { return new CustomExtListener(); } //② 新建监听模块 protected class CustomExtListener extends TestListener { //③ 重写 TestListener里的相关方法,完成相关扩展的功能 @Override public void afterTestMethod(Object testObject, Method testMethod, Throwable testThrowable) { … } @Override public void beforeTestMethod(Object testObject, Method testMethod) { … } } … }
在①处新建自定义扩展模块CustomExtModule,实现Module接口。在②处新建自定义监听模块,继承TestListener。在③处重写(@Override)TestListener里的相关方法,完成相关扩展的功能。实现自定义扩展模块之后,剩下的工作就是在Unitils配置文件unitils.properties中注册这个自定义扩展的模块:
unitils.modules=…,custom
unitils.module. custom.className= sample.unitils.module.CustomExtModule