Overall##

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

Junit的成功离不开其优美的架构设计,本着知其然,更要知其所以然的宗旨,希望用这个系列来记录下自己的对Junit4设计的理解。

如有不对或不足的地方,欢迎各位指正!

Junit4 的入口在哪?##

要想研究Junit4的源码,首先我们要知道Junit4的入口在哪里。

JUnitCore类是Facade模式的典型使用,它提供Static的方法去执行各种类型的Case, 包括JUnit 4 tests 和JUnit 3.8.x tests。设置命令行下,我们也可以调用这个JunitCore去执行Case.

很明显他就是这个Junit4的入口之一。

但是其他工具是如何继承Junit4的呢?###

以Maven为例,看看Maven是如何和Junit4集成的:

  1. private static void execute( Class<?> testClass, RunNotifier notifier, Filter filter )
  2. {
  3. final int classModifiers = testClass.getModifiers();
  4. // filter.shouldRunClass( testClass )
  5. if ( !Modifier.isAbstract( classModifiers ) && !Modifier.isInterface( classModifiers ) )
  6. {
  7. Runner runner = Request.aClass( testClass ).filterWith( filter ).getRunner();
  8. if ( countTestsInRunner( runner.getDescription() ) != 0 )
  9. {
  10. runner.run( notifier );
  11. }
  12. }
  13. }

(如需访问完整代码,点击这里)

其实如果我们跟进去JunitCore的runClasses方法,会发现,这Maven用的方式跟JunitCore的方式大同小异,其基本执行路劲都是 Request->Runer->runner.run()

所以要分析Junit4,我们得看看这三步是怎么走的。

Request 类##

Request类表示Case执行请求的抽象概念。也就是说当我们想让Junit4执行我们的Case时,实际上我们发送的就是这样一个请求。

Request允许执行单个case,也支持一次处理多条case,分别有不同的方法来Handle。

这里要注意的是Request是Abstract类,它有个Adstract方法:

  1. /**
  2. * Returns a Runner for this Request
  3. *
  4. * @return corresponding Runner for this Request
  5. */
  6. public abstract Runner getRunner();

这个方法很重要,从注释我们看到,它是想获得相应的能够处理我们期望Request的Runner。

那么这个Runner类到底是怎么实现的呢?

ClassRequest##

ClassRequest是Request的派生类,它实现了getRunner()抽象方法。

  1. @Override
  2. public Runner getRunner() {
  3. if (runner == null) {
  4. synchronized (runnerLock) {
  5. if (runner == null) {
  6. runner = new AllDefaultPossibilitiesBuilder(canUseSuiteMethod).safeRunnerForClass(fTestClass);
  7. }
  8. }
  9. }
  10. return runner;
  11. }

ClassRequest类比较直白,就是调用AllDefaultPossibilitiesBuilder类的safeRunnerForClass 方法去获得合适的Runner类。

再仔细看它的实现,这不就是采用双重检查加锁的单例模式嘛?!不过不是为了获得ClassRequest类的实例,而是为了获得合适的Runner。

AllDefaultPossibilitiesBuilder##

AllDefaultPossibilitiesBuilder类使用了Builder模式,safeRunnerForClass根据传入的类,来确定要用什么样的Runner来执行这条Case,不同的Runner适用于不同类型的Case

  1. @Override
  2. public Runner runnerForClass(Class<?> testClass) throws Throwable {
  3. List<RunnerBuilder> builders = Arrays.asList(
  4. ignoredBuilder(),
  5. annotatedBuilder(),
  6. suiteMethodBuilder(),
  7. junit3Builder(),
  8. junit4Builder());
  9. for (RunnerBuilder each : builders) {
  10. Runner runner = each.safeRunnerForClass(testClass);
  11. if (runner != null) {
  12. return runner;
  13. }
  14. }
  15. return null;
  16. }

而List中的ignoredBuilder,annotatedBuilder,suiteMethodBuilder,junit3Builder,和junit4Builder 都是RunnerBuilder类的子类,各自都实现了safeRunnerForClass方法,最终这些类自己会判断自己是否适合执行这些Case,如实合适就实例化相应的Runer并返回。

而Runner类最终调用Runner.run()方法来执行Case。

是不是很巧妙?

Junit4 High Level 类图##

下面是Junit4 High Level 调用顺序的类图,也算是对上面的一个总结:

从上面我们不难看出Junit4的调用路径:

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

设计模式知识补充##

Builder模式###

Builder模式又叫生成器模式,是一种对象构建模式,它把复杂对象的构建过程抽象出来,使这个抽象过程能够根据不同方式,构造出不同的对象。

维基百科的例子可以用下面的类图表示:

相比较而言,个人觉得Junit4的Builder模式运用的更巧妙些,AllDefaultPossibilitiesBuilder类本身既是RunnerBuilder的派生类,但是其也使用初始化RunnerBuilder类的所有派生类。

单例模式###

单例模式确保一个类只有一个实例,并提供一个全局访问点。

单例模式的写法很多,但是各种写法各有利弊:

  • 经典实现 缺点就是线程不安全
  • 方法级加Synchronized 性能有损失,因为方法只有在第一次执行时,才需要同步
  • 利用Static特性,JVM加载时初始化对象 也有缺点,因为如果对象比较大时,一直Hold住这个对象比较耗内存,起不到按需初始化的要求
  • 双重检查加锁这种方式还不错,上文说到ClassRequest的getRunner()方法就是使用这个
  • 内部类方法方式这种方式比较少见,但是很巧妙,使用静态式的成员内部类,该内部类只有在被调用时才会被JVM装载,从而实现了延迟加载

更多具体实现可以参见汤姆大叔的博客

敬请期待##

后续计划

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

Junit4 架构设计系列(1): Request,ClassRequest 和 RunnerBuilder的更多相关文章

  1. Junit4 架构设计系列(2): Runner.run()与Statement

    Overall 系列入口: Junit4 架构设计系列(1): Request,ClassRequest 和 RunnerBuilder 前文中,我们基本理清了Junit4执行Case大体上的Flow ...

  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. 推荐一个很好的富文本web编辑器UEditor

    前天产品提了一个编辑器的bug,本人找是找到了问题的症结,就是不好改.框架是压缩兼混淆后的代码.查一下,好多年前的框架... 咨询了一个同事有关旧框架的事情,他也建议我升级编辑器并帮忙帮我找了UEdi ...

  2. C#基础知识—父类和子类的关系

    基础知识一: using System; using System.Collections.Generic; using System.Linq; using System.Windows.Forms ...

  3. 通过源码看android系列之multidex库

    我们在开发项目时,喜欢引入好多的第三方包,大大的方便了我们的开发,但同时,因为android方法总数的限制,不能超过65k,然而呢,随着我们的开发,65k最终还是会超过,所以,google就给出了这个 ...

  4. 在String中添加移动构造函数和移动赋值运算符

    13.50 没有定义析构函数 #include<iostream> #include<string> #include<memory> #include<ut ...

  5. Windows Python 2.7 安装 Numpy

    为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/ShiJiaqi. http://www.cnblogs.com/shijiaqi1066/p/4846093. ...

  6. HTML+CSS基础学习笔记(6)

    一.元素分类 CSS中html的标签元素大体分为三种类型 1.块状元素 @特点: #每个块级元素都从新的一行开始,并且其后的元素也另起一行(一个块级元素独占一行) #元素的高度.宽度.行高以及顶和底边 ...

  7. JavaScript原型模式

    一.提到原型模式,和构造函数关系密切,先讲一下它 javascript没有类,通过函数来模拟实现类,用new来创建对象,函数内部的this指针来指向调用它的对象. 事例中创建对象myGril,这个对象 ...

  8. document library\ picture library\Asset Library的默认文件夹

    document library\ picture library\Asset Library的默认文件夹? document library 默认文件夹:Forms picture library默 ...

  9. EventBus3 简单使用及注意点

    博客: 安卓之家 微博: 追风917 CSDN: 蒋朋的家 简书: 追风917 # EventBus3 简介 EventBus Android 发布/订阅事件总线,可简化 Activities, Fr ...

  10. A题笔记(2)

    No. 1505 文件读相关 #include <fstream> #include <sstream> <fstream>支持文件的IO <sstream& ...