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

第二节里我们介绍了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 {

}





  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值