您的位置:首页 > 其它

一个通用的单元测试框架的思考和设计03-实现篇-核心类源码

2011-07-30 11:56 916 查看
第二节里我们介绍了iunit整体的设计思路以及核心类之间的关系,这篇将以源码+解释的方式来演示核心类的实现方式

1.IUnitRunner 类

这个类是测试的入口类,直接继承自junit4.8的BlockJunit4ClassRunner,在构造函数里,我们把iunit框架的扩展功能添加了进来,因为整个框架呃设计都是基于Listener的,所以只需要把监听器在框架运行的时候加载进来即可--见构造函数,listener的注册是通过注解来进行的,因为测试类本身可能会有继承关系,因此需要遍历父类中的Listener,把子类+所有父类中的Listener合并起来,当然还要注意剔除掉重复注册的Listener,否则很可能导致一个Listener被执行多次(既在子类中注册过了又在父类中注册过了)

package com.crazycoder2010.iunit;

import java.util.ArrayList;
import java.util.List;

import org.junit.Ignore;
import org.junit.internal.AssumptionViolatedException;
import org.junit.internal.runners.model.EachTestNotifier;
import org.junit.runner.Description;
import org.junit.runner.notification.RunNotifier;
import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;
import org.junit.runners.model.Statement;

import com.crazycoder2010.iunit.annotation.IUnitTestExecuteListeners;

public class IUnitRunner extends BlockJUnit4ClassRunner {
/**
* 监听器
*/
private List<IUnitTestExecuteListener> executeListeners = new ArrayList<IUnitTestExecuteListener>();
private Class<?> clazz;
private TestContext testContext;
public IUnitRunner(Class<?> klass) throws InitializationError {
super(klass);

//这个构造函数是junt的调用入口,这里我们把扩展功能的初始化写到其后
this.clazz = klass;
this.testContext = new TestContext();
initListeners();
}

private void initListeners(){
this.executeListeners.addAll(findListeners());
}

/**
* 解析为当前测试类注册的监听器
* @return
*/
@SuppressWarnings("rawtypes")
private List<IUnitTestExecuteListener> findListeners(){
List<IUnitTestExecuteListener> result = new ArrayList<IUnitTestExecuteListener>();
List<Class> listeners = new ArrayList<Class>();
Class<?> c = this.clazz;
while(c != null){
IUnitTestExecuteListeners listener = c.getAnnotation(IUnitTestExecuteListeners.class);
if(listener != null){
for(Class<? extends IUnitTestExecuteListener> l : listener.value()){
if(!listeners.contains(l)){
listeners.add(l);
}
}
}
c = c.getSuperclass();
}
for(Class clazz:listeners){
try {
result.add((IUnitTestExecuteListener) clazz.newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return result;
}

@Override
protected Object createTest() throws Exception {
Object testInstance = super.createTest();
//加上我们框架的扩展功能
this.testContext.setTestInstance(testInstance);
for(IUnitTestExecuteListener executeListener : this.executeListeners){
executeListener.prepareTestInstance(testContext);
}
return testInstance;
}

@Override
protected void runChild(FrameworkMethod method, RunNotifier notifier) {
EachTestNotifier eachNotifier= makeNotifier(method, notifier);
if (method.getAnnotation(Ignore.class) != null) {
runIgnored(eachNotifier);
} else {
runNotIgnored(method, eachNotifier);
}
}

private EachTestNotifier makeNotifier(FrameworkMethod method,
RunNotifier notifier) {
Description description= describeChild(method);
return new EachTestNotifier(notifier, description);
}

private void runIgnored(EachTestNotifier eachNotifier) {
eachNotifier.fireTestIgnored();
}

private void runNotIgnored(FrameworkMethod method,
EachTestNotifier eachNotifier) {
eachNotifier.fireTestStarted();
try {
Statement statement = methodBlock(method);
doBefore();
statement.evaluate();
doAfter();
} catch (AssumptionViolatedException e) {
eachNotifier.addFailedAssumption(e);
} catch (Throwable e) {
eachNotifier.addFailure(e);
testContext.setThrowable(e);
doAfterThrowable();
} finally {
eachNotifier.fireTestFinished();
}
}

private void doBefore()throws Exception{
for(IUnitTestExecuteListener executeListener : this.executeListeners){
executeListener.beforeTest(testContext);
}
}

private void doAfter() throws Exception{
for(IUnitTestExecuteListener executeListener : this.executeListeners){
executeListener.afterTest(testContext);
}
}

private void doAfterThrowable(){
for(IUnitTestExecuteListener executeListener : this.executeListeners){
try {
executeListener.afterThrowable(testContext);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}


2.IUnitTestExecuteListener接口

这个接口定义了测试用例执行生命周期的几个关键点

package com.crazycoder2010.iunit;

/**
* 框架执行监听器
* @author Kevin
*
*/
public interface IUnitTestExecuteListener {
/**
* TestCase对象被加载后执行的操作,每个TestCase只会执行一次
* @param testContext
*/
public void prepareTestInstance(TestContext testContext) throws Exception;

/**
* 在每执行一个单元测试方法之前运行
* @param testContext
* @throws Exception
*/
public void beforeTest(TestContext testContext) throws Exception;

/**
* 每个单元测试方法执行完时执行
* @param testContext
* @throws Exception
*/
public void afterTest(TestContext testContext) throws Exception;

/**
* 发生异常时做的处理
* @param testContext
*/
public void afterThrowable(TestContext testContext) throws Exception;
}
3.IUnitTestExecuteListeners注解

这个用来给测试类注册监听器的注解,一个IUnitTestExecuteListeners可以注册多个Listener

package com.crazycoder2010.iunit.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import com.crazycoder2010.iunit.IUnitTestExecuteListener;

/**
* 为TestCase注册监听器
*
* @author Kevin
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
@Documented
public @interface IUnitTestExecuteListeners {
/**
* 实际用到的监听器类
*
* @return
*/
Class<? extends IUnitTestExecuteListener>[] value() default {};
}
4.AbstractIUnitTestCase测试基类

这个类是为了便于测试定义了一个TestCase的基类,可以在此注册一些通用的监听器,注意@Runwith(IUnitRunner.class)这个是关键,否则我们写在runner中扩展的功能是不会被junit4执行到的

package com.crazycoder2010.iunit;

import org.junit.runner.RunWith;

import com.crazycoder2010.iunit.annotation.IUnitTestExecuteListeners;

@RunWith(IUnitRunner.class)
@IUnitTestExecuteListeners({DatasetProviderListener.class})
public class AbstractIUnitTestCase {

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐