一个通用的单元测试框架的思考和设计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 {}
- 一个通用的单元测试框架的思考和设计03-实现篇-核心类源码
- 一个通用的单元测试框架的思考和设计04-实现篇-自动管理测试数据
- 一个通用的单元测试框架的思考和设计09-实现篇-视图操作
- 一个通用的单元测试框架的思考和设计01-思考篇
- 一个通用的单元测试框架的思考和设计05-实现篇-Guice框架启动与自动注入
- 一个通用的单元测试框架的思考和设计02-设计篇
- 一个通用的单元测试框架的思考和设计08-实现篇-在testcase代码中执行sql语句
- 一个通用的单元测试框架的思考和设计06-实现篇-自动管理测试数据-如何临时关闭数据外键约束
- 一个通用的单元测试框架的思考和设计07-实现篇-自动管理测试数据-如何为自增长主键id赋值
- 通用权限实现的核心设计思想
- 通用权限实现的核心设计思想
- 通用权限实现的核心设计思想
- DbHelperV2 - Teddy的通用数据库访问组件设计和思考
- DbHelperV2 - Teddy的通用数据库访问组件设计和思考
- 关于裁剪单元测试过程的一个思考
- 微信群控系统源码的实现原理,核心源码实现,核心框架。
- 单元测试框架的简单实现
- 12.Tornado TCPServer类的设计解读 (副标题: 一个通用的server框架)
- 函数指针与指针函数
- HDOJ/HDU 3877 Special sort
- 不错的CSS教程
- ipad和ipad 2的参数比较
- linux下Makefile的automake生成全攻略
- 一个通用的单元测试框架的思考和设计03-实现篇-核心类源码
- 让托盘弹出菜单失去焦点时自动消失
- 用autotools制作Makefile 和configure文件。
- IOCP模型总结
- mysql及oracle中的group_contact小结
- java 7中新增的CPU和负载的监控
- Dir的用法批量记录文件名记入记事本txt
- java 7几个新特性快速扫描
- IOCP , kqueue , epoll ... 有多重要?