Luna Tech | Junit5 Study Notes

April 19, 2019 字数 3180 7 min


0. 前言

上周深刻反省了一下自己的学习效率,觉得我学东西经常不求甚解,对于底层知识和整体的架构理解的不是很到位。

有时候只要程序能够运行或者达到课程要求,我就满足了,却没有花时间认真去看文档;过去的经验没有认真去总结提炼,导致下一次遇到类似的问题还是要花很长时间去解决。

我一直觉得自己是个努力的人,但我也深刻地认识到:努力需要技巧。

我学习 IT 的时间不长,学习新概念、消化新知识的时候需要花费更多的时间精力,而且经常是学了 A 忘了 B,一度对自己非常沮丧失望。

我不希望自己永远是个“用蛮力”学习的人,我希望自己能花时间去钻研知识,并且能用自己的话复述出来,认真思考知识之间的关联,而不是满足于“程序只要能运行就 OK“。

比如 FIT5171 这门课,用到了 JUnit5。但学期过半,我都没有认真学过这个 Test framework,就算所有 Assignment 都做了,考试也能 pass,我自己的知识体系还是支离破碎的,而这对于我今后的职业发展并没有好处。

痛定思痛,我前几天开始在 Lynda(https://www.lynda.com/)学习关于JUnit 5 的一门课,并且有意放慢学习的速度(老老实实听 1 倍速,认真研究每张 PPT),并且做笔记,加入自己的思考。

今天这篇文章就是基于我这些天的 JUnit 5 学习笔记。课程是英文的,但我觉得中文是母语,一开始先用中文表达清楚,并且能把中英文的术语对应起来,也对之后查阅资料有帮助,所以笔记主要是中文,个别术语是英文。

主要涉及的知识点有:

  1. JUnit 5 和 JUnit 4 的区别;

  2. JUnit 5 的三个 Component;

  3. JUnit 5 能和哪些 IDE/Build tool 结合使用;

  4. JUnit 5 Platform 的作用;

  5. JUnit 5 Platform 的两个 API:Launcher API 以及 TestEngine API


1. 什么是 JUnit 5 ?它和 JUnit 4 有什么区别?

JUnit 4 和 JUnit 5 都是 Java 系的测试框架,JUnit 5 是 JUnit 4 的升级版。

JUnit 4 是基于 Java 5 的 Test framework,但是模块化 modularisation 做的很不好(只有一个 junit.jar 的包),test discovery 和 test execution 是没有分离的,对 integration test 和 system test 的支持不足,也不支持 extension。

JUnit 4 缺点举例

1. @RunWith: Junit4 runners are not composable(不能组合的)

  • Runner 在 JUnit 4 里面是个 Java class,它的作用是管理当前这个 test case 的 life-cycle,包括 instantiation,calling setup,其他 methods,runtests,handle exceptions,send notification 等等(可以看出来这个 class 承担了非常多的任务,一方面它非常强大 powerful ,也能体现出 Junit4 欠缺 modularisation,也就是前面提到的 not composable)。
  • 下图中我们可以看到 Parameterized test 不能和 Spring test support 相结合使用,所以两个 test case 分别要去 implement runner。
  • 而 JUnit 5 对 Runner 这个 API 进行了改进。

JUnit4 Limitations-1

2. @Rule and @ClassRule: JUnit 4 不能在 method-level 和 class-level 设置同一个 Rule。

• **Rule 是用来干嘛的?**是对于 Test behavior 的一些 customization。

• can use a single rule for method level and class level 是很重要的,尤其是需要在 framework(比如 Spring)中进行 model development 的时候。

JUnit4 Limitations-2

Note:我目前不是很明白这个部分。好像是 JUnit 4 的 method level rule 要通过 Rule 这个 API 来定义,但是 Class level rule 要通过 ClassRule API 来定义,不能用同一个 API 同时定义 ClassRule 和 MethodRule。

由于上面提到的 JUnit 4 的种种缺陷,Community 就有了搞 JUnit 5 的 motivation。

但是没有钱没有人啊,怎么办呢?

这时候一些人就发起了 Junit Lambda campaign,向公众集资,筹到钱之后组织了一批来自各大公司的 developer(比如 intelliJ,Gradle,Eclipse)来一起搞 JUnit 5。

JUnit 5 是如何克服 JUnit 4 的问题的呢?

  1. 它是基于 Java8 来写的。

  2. 它包括三个 Module(Platform + Jupiter + Vintage);

    • Platform 是用来在 JVM 上启动 testing framework 的

    • Jupiter 是 JUnit 5 的主要部分,它包括新的 programming 和 extension model

    • Vintage 是用来支持 JUnit 3 和 JUnit 4 的

  3. JUnit 5 支持 Extension(有 Extension API)-> @ExtendWith(…)

  4. Junit5 是 Forward and backward compatible 的:用 JUnit 4 写的 code 也能在 platform 上运行

    • 比如用 @RunWith(JUnitPlatform.class) 这个 annotation 就可以运行在 JUnit 4 的环境下运行 JUnit 5 的代码。

JUnit 5 in a Nutshell


2. JUnit 5 架构:Vintage + Jupiter + Platform

之前提到 JUnit 5 是模块化 modularisation 的,它的框架中包含 Vintage,Jupiter 和 Platform 三个部分:

JUnit 5 architecture

  1. Platform 是用来让所有 Test framework 能在 JVM 中运行的;

  2. 在 Platform 的支持下,Vintage 这个 Component,可以用来运行 JUnit 3 和 JUnit 4 的 test case 代码(对旧版本的兼容);

  3. 同时 Jupiter 提供了一个新的 Programming model,让我们可以写 JUnit 5 的 test case;

  4. JUnit 5 platform 还支持用第三方的 framework 来写 test case,比如:

    1. Cucumber: 一个 BDD (Behavior Driven Development) framework,可以用来测试 UI
    2. **Spock:**一个 testing framework,可以用来测试 Java 和 Groovy(一种编程语言)写的 Application
    3. **Fitnesse:**一个用来做 Acceptance Test 的测试工具

在这幅图的最下面是 IDEs/build tools:

  1. Maven 和 Gradle 是 build tool(项目自动构建工具),用来让开发、测试、部署、分发的流程更加自动化,节约开发者的时间;开发者只需要自定义构建逻辑就可以了,不需要手动去操作每个步骤,也减少了出错的几率。

    • Maven 是一个老牌项目管理工具;
    • Gradle 是后起之秀,基于 Groovy 语言,更加简洁和灵活;
    • Maven 和 Gradle 的比较:
      • Maven 使用.pom 文件,基于 XML;Gradle 使用.gradle 文件,基于 Groovy。
      • 同样引用一个 dependency,Maven 需要的代码量比 Gradle 来的多,Maven 的配置更加复杂。
  2. IntelliJ 和 Eclipse 这些 IDE 也可以和 JUnit5 platform 一起使用。


3. JUnit 5 API 详解

下面这幅图更加详细的展示了 JUnit5 的内部结构。

JUnit 5 Architecture

首先,**Platform(中间黄色部分)**是介于 Test case 和 Build tool 之间的,它主要包含两个 API:

  1. 一个是 junit-platform-engine,它里面包含 TestEngine API,所有基于 JVM 的 Test Framework 都可以在 platform 运行(例如:JUnit4,JUnit5 和第三方框架,也就是图中 Platform 上方的三个部分)。

  2. 另一个是 junit-platform-launcher,它里面包含一个 Launcher API,可以用来发现和执行测试。

    • 这个 API 通常会被 IDE 和 build tools 调用(就是 Platform 下方的部分);

    • IDE 和 build tool 可以用这个 API 来发现测试,筛选测试,执行测试;

    • 这样一来,JUnit5 platform 就把这部分的职责剥离出来,让 IDE 和 build tool 去负责这些 process,更加 loose couple(克服了 JUnit 4 的缺点)。

  3. Launcher API 中还包含一些其他的 Artifacts(人工产物,产品,工件):

    • junit-platform-runner 定义了 runner 和一些 annotation(注解,例如@RunWith..),目的是为了配置和运行 JUnit4 的测试代码;

    • junit-platform-console 让我们可以用 command line(命令行)发现并且执行测试代码;

    • junit-platform-surefire-provider 让我们可以通过 Maven 的 Surefire 插件执行测试代码;这个 surefire 插件在执行 unit test 单元测试之后可以生成两种形式的 test result 报告(txt 和 xml)

    • junit-platform-gradle-plugin 是和 maven 的 surefire plugin 相对应的(之前提到 JUnit5 同时支持 Gradle 和 Maven 两种 build tool)。

**Vintage(左上方绿色部分)**包含 junit-vintage-engine,这个 layer 是用于执行 JUnit4 的 test 的。

  • platform 里面有个 TestEngine API,不同的 test framework 里面也有自己对应的 API implementation。

**Jupiter(最上层中间红色部分)**也有一个 TestEngine implementation,还有 junit-jupiter-api,这是让我们可以写 test case 的 API,包含所有 JUnit5 的 programming model(也就是说 JUnit5 的核心部分在这里)。

**第三方 Test Framework(最上层右边蓝色部分)**只要 implement TestEngine 这个 API 就可以接入 JUnit platform 啦~

👆 以上就是 JUnit 5 的整体架构介绍,下面再谈一下 Launcher API,这个 API 在 JUnit 5 Framework 中起着非常重要的作用。


4. Launcher API 详解

Launcher API-1

这个 API 里面有个 java class 叫做 LauncherDiscoveryRequest,这个 Class 的作用是让我们发现和筛选测试(discover and filter tests)。

  • 上图中的 selector method 中,我们可以 pass classes, methods, package 从而找到要做的测试 discover test;
  • 然后我们可以再用一个 filters method 来进一步筛选测试;

另外,这个 API 中还有个 TestExecutionListener class,它是用来执行测试的,也可以用于生成报告。

Launcher API-2

总结

Launcher API 具有承上启下的作用,它提供 Maven 和 Gradle 的接口,提供 Junit4 的 runner,还有 Console Launch 这个 implementation,让我们可以用在控制台 console 用命令行 command line 启动 JUnit5 platform。

Launcher API-3


Talk to Luna


Support Luna