Unitils框架与模块扩展
Unitils这个Java开源包的目的是让单元测试变得更加容易和维护。Unitils构建在DBUnit与EasyMock项目之上并与JUnit和TestNG相结合,支持数据库测试,支持利用mock对象进行测试并提供与Spring和Hibernate相集成。Unitils设计成以一种高度可配置和松散偶合的框架来添加这些服务到单元测试中,其模块化的管理方式使Unitils更易进行扩展。
1,JUnit4ClassRunner简介
所有的测试方法test method都是在Runner下执行的,可以将Runner理解为junit运行的容器,默认情况下junit会使用JUnit4ClassRunner作为所有test
method的执行容器。如果要定制自己的junit,则可以实现自己的Runner,最简单的办法就是Junit4ClassRunner继承,spring-test,unitils这些框架就是采用这样的做法。如在spring中是SpringJUnit4ClassRunner,在unitils中是UnitilsJUnit4TestClassRunner。
JUnit4ClassRunner的构造器如下:
public JUnit4ClassRunner(Class< ?> klass) throws
InitializationError {
fTestClass= new TestClass(klass);
fTestMethods= getTestMethods();
validate();
}
JUnit4ClassRunner没有默认的构造器, 从构造器代码中我们可以看出, 它需要一个参数,
这个参数就是我们当前要运行的测试类test class, Runner拿到了要执行的测试类之后, 就可以进一步拿到需要执行的测试方法,
这些动作是通过注解获得。这个测试的运行是通过runner中的run()方法执行:
@Override
public void run(final RunNotifier notifier) {
new ClassRoadie(notifier, fTestClass, getDescription(),
new Runnable() {
public void run() {
runMethods(notifier);
}
}).runProtected();
}
这个run方法里就会按照junit定义的顺序依次执行被@BeforeClass、@Before、@Test、@After、@AfterClass标记的方法。
2,UnitilsJUnit4TestClassRunner
Unitils的runner在继承了Junit4 runner的基础上进一步改写了run()方法来定义自己的执行顺序:
从图中可以看到,整个Unitils在运行期间,在执行Junit4的@BeforeClass前会执行Unitils的beforeTestClass方法,然后执行被Junit4的@BeforeClass标签标注的方法,再依次执行afterCreateTestObject、beforeTestSetUp、@Before……等方法。
除去Junit4原本执行方法顺序,Unitils的runner继承了Junit4 的runner并改写,在原有基础上加入了融入自己的执行方法顺序,这些新增加的自定义方法写在了一个名为TestListener的抽象类中。UnitilsTestListener继承了此TestListener并改写了这些方法,如执行UnitilsTestListener
的beforeTestClass方法时,Unitils会通过读取配置的module依次遍历每个module的
TestListener并执行TestListener里的beforeTestClass方法。如下图所示:
因此,便形成了Unitils如下的组件体系:
Unitils默认提供了如下组件:
1,DatabaseModule 数据库单元测试的维护和连接池。
2,DbUnitModule 使用DBUnit来管理测试数据。
3,Hibernatemodule 支持Hibernate的配置和自动数据库映射检查。
4,EasyMockModule 支持创建mock和宽松的反射参数匹配。
5,InjectModule 支持在一个对象中注入另一个对象。
6,SpringModule 支持加载spring的上下文配置,并检索和Spring Bean注入。
这些组件都是通过Unitils的默认配置文件定义加载的。
3,Unitils配置文件
unitils-default.properties
默认的配置,它包含了缺省值并被包含在unitils的发行包中;
unitils.properties
可包含项目的全部配置;
unitils-local.properties
可以包含用户特定配置;
unitils-default.properties配置文件包含配置内容基本如下:
这里的配置定义了一般配置文件的名字unitils.properties和用户自定义配置文件unitils-local.properties,并给出了默认的模块及模块对应的className,便于Unitils加载对应的模块module。但是如果用户在unitils.properties文件中定义了相应的不同配置,将会以unitils.properties配置内容为主。
4,Unitils模块扩展
扩展Unitils一般分四步走:
①,新建功能Module(如AModule),实现Module接口;
②,新建模块Listener(如AListener),继承TestListener;
③,改写(@Override)TestListener里的相关方法,完成相关扩展的功能;
④,修改配置文件unitils.properties,注册扩展的模块:
unitils.modules=… …,a
unitils.module. a.className=… … AModule
笔者期望在测试方法执行之后清理用Excel准备的测试数据,保证数据的健壮性和避免测试数据的相互冲突,笔者如下扩展了DbUnitModule来完成这个功能:
public class TDbUnitModule extends DbUnitModule {
……
@Override
public TestListener getTestListener() {
final TestListener listener = super.getTestListener();
TestListener result = new TestListener() {
@Override
public void beforeTestSetUp(Object testObject, Method
testMethod) {
listener.beforeTestSetUp(testObject, testMethod);
}
@Override
public void afterTestMethod(Object testObject, Method
testMethod, Throwable testThrowable) {
// 清理测试数据
deleteDataSet(testMethod, testObject);
listener.afterTestMethod(testObject, testMethod, testThrowable);//比较
}
};
return result;
}
}
修改unitils.properties:
unitils.modules=database,dbunit,mock,easymock,inject,spring
unitils.module.dbunit.className=com.taobao.unitils.module.TDbUnitModule
至此,模块扩展基本完成了。
|