Overall##

系列入口:

Junit4 架构设计系列(1): Request,ClassRequest 和 RunnerBuilder

前文中,我们基本理清了Junit4执行Case大体上的Flow:

Request -> ClassRequest.getRunner() -> AllDefaultPossibilitiesBuilder.safeRunnerForClass() -> runner.run()

并且介绍了类Request,ClassRequest,和RunnerBuilder,剩下runner.run()没讲,那本文就从这说起。

Runner.run()是如何执行Case的?##

run()方法是抽象类Runner定义的一个方法,目的就是执行Case。各个Runner的子类都要实现这个方法。

 /**
* Run the tests for this runner.
*
* @param notifier will be notified of events while tests are being run--tests being
* started, finishing, and failing
*/
public abstract void run(RunNotifier notifier);

从前文知道,默认负责执行Junit4风格case的Runner是BlockJUnit4ClassRunner, 但是BlockJUnit4ClassRunner并不是直接继承与Runner类,而是中间多了一层ParentRunner<T>, 如下图:

ParentRunner类负责filter 和 sort Test Class, 处理 @BeforeClass and @AfterClass 方法, 和各种 ClassRules, 并且按顺序执行Test Class。

那我们来看看ParentRunner是如何执行Junit4风格的Test Class的,具体实现如下:

@Override
public void run(final RunNotifier notifier) {
EachTestNotifier testNotifier = new EachTestNotifier(notifier,
getDescription());
try {
Statement statement = classBlock(notifier);
statement.evaluate();
} catch (AssumptionViolatedException e) {
testNotifier.addFailedAssumption(e);
} catch (StoppedByUserException e) {
throw e;
} catch (Throwable e) {
testNotifier.addFailure(e);
}
}

第一行是实例化一个EachTestNotifier,主要为了记录执行过程的,这里暂且不做详细解释。

很明显,Try里的逻辑才是真正的执行步骤。逻辑也很清晰,就是得到Statement,然后调用evaluate()方法。

继续跟进方法classBlock,我们就会看到下面的逻辑:

protected Statement classBlock(final RunNotifier notifier) {
Statement statement = childrenInvoker(notifier);
if (!areAllChildrenIgnored()) {
statement = withBeforeClasses(statement);
statement = withAfterClasses(statement);
statement = withClassRules(statement);
}
return statement;
}

这个逻辑是用于组合我们期望的Statement,不难看出,Runner.run()方法主要涉及下面几个执行逻辑:

  • childInvoker方法表示要执行执行这条Test Class了
  • withBeforeClasses则是判断,Test Class有没有被@BeforeClass修饰的方法?要是有的话就要先执行它
  • withAfterClasses则判断Test Class有没有@AfterClass修饰的方法
  • withClassRules这个则是检测Test Class有没有适用TestRules

当所有的statement包装好后,调用statement.evaluate()就可以按要求,按顺序的执行我们希望的结果了。

看到这种嵌套输入与输出的写法,会不会有中恍然大悟的感觉?!这不就是装饰者模式的经典适用场景嘛。

要注意的是,这里处理的都是Class,而对于Method级,实际上也有类似装饰者模式的适用场景, 我们从上面childInvoker跟进去,最终会发现,真正执行Test Method的是BlockJunit4ClassRunner类,首先它实现了ParentRunner的抽象方法runChild:

//
// Implementation of ParentRunner
// @Override
protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
Description description = describeChild(method);
if (isIgnored(method)) {
notifier.fireTestIgnored(description);
} else {
Statement statement;
try {
statement = methodBlock(method);
}
catch (Throwable ex) {
statement = new Fail(ex);
}
runLeaf(statement, description, notifier);
}
}

然后在期方法块中methodBlock组合statement:

protected Statement methodBlock(final FrameworkMethod method) {
Object test;
try {
test = new ReflectiveCallable() {
@Override
protected Object runReflectiveCall() throws Throwable {
return createTest(method);
}
}.run();
} catch (Throwable e) {
return new Fail(e);
} Statement statement = methodInvoker(method, test);
statement = possiblyExpectingExceptions(method, test, statement);
statement = withPotentialTimeout(method, test, statement);
statement = withBefores(method, test, statement);
statement = withAfters(method, test, statement);
statement = withRules(method, test, statement);
return statement;
}

跟ClassBlock异曲同工。

详解Statement##

Statement在Junit4中是非常重要的一块,完全可以大书特书,如图:

在JUnit4中,一切动作都可以用Statement来表示,从上图包的分配方式我们可以看到,Junit4不但预定义了一些必须使用或者使用频率高的动作,如

  • InvokeMethod 执行Test Method
  • RunBefores 先于InvokeMethod之前执行@Before方法
  • RunAfters 执行@After方法
  • Fail Throw Errors
  • FailOnTime 设置Timeout的入口
  • ExpectException 定义期望所抛出的异常

还定义了RunRules,能够让我们Reuse或者是重新定义自己的Rule.可谓方便至极.

设计模式知识补充##

Decorator模式###

Decorator(装饰者)模式动态地将责任添加到对象上。在扩展功能方面,装饰者模式提供了比继承更有弹性的替代方案。

这是我实现的标准装饰者模式的类图:

而装饰者模式特点就是:

  • 装饰者和被装饰者对象有相同的超类型
  • 我们可以用多个装饰者去包装一个对象
  • 装饰者可以在所委托被装饰者的行为之前/或之后,就上自己的行为,以达到特定的目的
  • 对象可以在运行时动态地,不限量的用装饰者来装饰

高亮的第三点很关键,我们知道Junit4之所以能让 @BeforeClass 和 @AfterClass 等注解,按要求或前或后的执行,就是利用了这一点。

敬请期待##

后续计划

  • Junit4架构设计系列(3) RunNotifer

童鞋,如果觉得本文还算用心,还算有用,何不点个赞呢(⊙o⊙)?

Junit4 架构设计系列(2): Runner.run()与Statement的更多相关文章

  1. Junit4 架构设计系列(1): Request,ClassRequest 和 RunnerBuilder

    Overall Junit的成功已不言而喻,其广泛应用于单元测试,测试驱动开发领域.大量的工具,IDE都集成了JUnit,著名的有Maven,Ant,Eclipse,甚至像Google SDK提供的A ...

  2. 图解 kubernetes scheduler 架构设计系列-初步了解

    资源调度基础 scheudler是kubernetes中的核心组件,负责为用户声明的pod资源选择合适的node,同时保证集群资源的最大化利用,这里先介绍下资源调度系统设计里面的一些基础概念 基础任务 ...

  3. iOS架构设计系列之解耦的尝试之变异的MVVM

    最近一段时间,在思考如何合理的架构一个可扩展性良好的界面编程方式.这一部分的成果做成了一个叫ElementKit的库.目前功能在不断的完善中. 关于iOS的架构,看多了MVVM,VIPER,MVC,M ...

  4. 架构设计系列-前端模式的后端(BFF)翻译PhilCalçado

    本文翻译自PhilCalçado的官网:https://philcalcado.com/2015/09/18/the_back_end_for_front_end_pattern_bff.html 对 ...

  5. GPS部标监控平台的架构设计(十一)-基于Memcached的分布式Gps监控平台

    部标gps监控平台的架构,随着平台接入的车辆越来越多,架构也面临越来越大的负载挑战,我们当然希望软件尽可能的优化并能够接入更多的车辆,减少在硬件上的投资.但是当车辆增多到某一个临界点的时候,仍然要面临 ...

  6. Angular应用架构设计-3:Ngrx Store

    这是有关Angular应用架构设计系列文章中的一篇,在这个系列当中,我会结合这近两年中对Angular.Ionic.甚至Vuejs等框架的使用经验,总结在应用设计和开发过程中遇到的问题.和总结的经验, ...

  7. 架构设计 | 基于Seata中间件,微服务模式下事务管理

    源码地址:GitHub·点这里 || GitEE·点这里 一.Seata简介 1.Seata组件 Seata是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务.Seata将为用 ...

  8. MyBatis架构设计及源代码分析系列(一):MyBatis架构

    如果不太熟悉MyBatis使用的请先参见MyBatis官方文档,这对理解其架构设计和源码分析有很大好处. 一.概述 MyBatis并不是一个完整的ORM框架,其官方首页是这么介绍自己 The MyBa ...

  9. .net core实践系列之短信服务-架构设计

    前言 上篇<.net core实践系列之短信服务-为什么选择.net core(开篇)>简单的介绍了(水了一篇).net core.这次针对短信服务的架构设计和技术栈的简析. 源码地址:h ...

随机推荐

  1. 关于ABAP事件的一张图

    事件: 这里有几组事件关键字 ,这些事件关键字在特定环境下控制ABAP/4 程序流. 逻辑数据库 是典型报表程序的外部流控制的中心点.如果将逻辑数据库链接到报表 程序,将导致显示选择 屏幕,并决定系统 ...

  2. flash builder4.7安装git插件

    如果直接点击Help > Eclipse Marketplace,然后搜索Egit, 以这种方式安装是会失败的!!! 因为版本兼容的问题.依次点击Help > About Flash Bu ...

  3. WCF客户端和服务器时间不一致,导致通道建立失败的问题)

    本文转载:http://www.cnblogs.com/bcbr/articles/2288374.html 最近,经常有客户反应,前天还用的好好的系统,今天就不能用了. 考虑到系统近来没有做过改动和 ...

  4. easyui常用控件及样式收藏

    CSS类定义: div easyui-window                               window窗口样式 属性如下: 1)       modal:是否生成模态窗口.tru ...

  5. 跟我一步一步开发自己的Openfire插件

    http://www.blogjava.net/hoojo/archive/2013/03/07/396146.html 跟我一步一步开发自己的Openfire插件 这篇是简单插件开发,下篇聊天记录插 ...

  6. BP神经网络基本原理

    2.1 BP神经网络基本原理 BP网络模型处理信息的基本原理是:输入信号Xi通过中间节点(隐层点)作用于输出节点,经过非线形变换,产生输出信号Yk,网络训练的每一个样本包含输入向量X和期望输出量t,网 ...

  7. HDOJ--4869--Turn the pokers【组合数学+高速幂】

    链接:http://acm.hdu.edu.cn/showproblem.php?pid=4869 题意:有m张扑克.開始时所有正面朝下.你能够翻n次牌,每次能够翻xi张.翻拍规则就是正面朝下变背面朝 ...

  8. RAID 2.0

      传统的RAID 1.0: 选几个硬盘—>做成RAID 5—>根据容量创建LUN—>映射给主机 1.重建时间随单盘容量的增大而迅速增加 2.性能上,一个LUN的读写只能在一个磁盘组 ...

  9. MYSQL 专家 ----zhaiwx_yinfeng

    http://mysqllover.com/?p=708 https://yq.aliyun.com/articles/54454 http://blog.csdn.net/zhaiwx1987/ar ...

  10. Vim程序编辑器

    Vim的三种模式: 1) 一般模式 以 vi 打开一个档案就直接进入一般模式了(这是默认的模式).在这个模式中, 你可以使用『上下左右』按键来移动光标,你可以使用『删除字符』或『删除整行』来处理档案内 ...