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

来源:互联网 发布:杭州数据分析师事务所 编辑:程序博客网 时间:2024/04/29 13:18
第二节里我们介绍了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;}@Overrideprotected Object createTest() throws Exception {Object testInstance = super.createTest();//加上我们框架的扩展功能this.testContext.setTestInstance(testInstance);for(IUnitTestExecuteListener executeListener : this.executeListeners){executeListener.prepareTestInstance(testContext);}return testInstance;}@Overrideprotected 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@Documentedpublic @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 {}





原创粉丝点击