拾贝

JUnit 5的前世今生

    arch     原创·测试

起源

前事不忘,后事之师。–《战国策·赵策一》

对Java程序员而言,JUnit无疑是使用最广泛的单元测试框架。自2006年初JUnit 4发布之后,11年间陆陆续续更新了13个小版本,最新的4.12版本是在2014年底发布的。在现今新技术、新框架层出不穷的IT圈,JUnit的版本更新速度不可谓不缓慢,这一点上,和去年同期发布的Jenkins 2.0如出一辙,后者也花了11年才升级了一个大版本。让我们回到2006年初,看看当时的Java程序员都玩些啥?

  • Java 5发布1年多,很多公司还在使用JDK 1.4,离Java 6正式发布还有大半年,更别提Java 8了。
  • Spring初出茅庐,离第一桶金Jolt和JAX大奖还有几个月,离2.0发布还有大半年,更别提Spring Boot和Spring Cloud了。
  • Maven比Spring老道一点,不过也只是2.0 Beta阶段,更别提Maven 3了。

了解了JUnit 4的出生背景,就不难理解如今JUnit 4的问题所在了。

  • Extension:相对于其应用的体量,JUnit的可扩展性不可谓不粗糙。Runner和Rules是扩展JUnit的主要手段,前者虽然强大,但是粒度太粗,扩展者需要从零实现全测试周期的支持,并且一个单元测试类只能绑定一个Runner,无法同时使用多个Runner。Rules虽然解决了粒度的问题,但扩展能力非常有限,只能作用于测试周期的特定阶段。
  • Modularity:在Maven之前,一个Java应用依赖的Jar包往往都是人工进行管理的,既繁琐又容易出错,因此,那个时候all-in-one风格的fat Jar更受程序员欢迎。但是当Maven,Ivy以及之后Gradle接管了应用的依赖管理之后,模块化成为了主流,以前的fat Jar由于包的大小、更容易导致依赖冲突等原因逐渐的不再受到青睐,甚至被打入冷宫。
  • Java 8:距离Java 8正式发布已经整整过去3年,Java程序员也渐渐习惯了使用Lambda表达式,对于一些新生代的Java程序员而言,没有Lambda表达式甚至都不会写Java代码了。

以上这些局限就构成了JUnit 5的起源。除此之外,JUnit 5还有更大的野心,JUnit as a Platform。

架构

你的是我的,我的还是我的。

什么是JUnit as a Platform?既然敢叫Platform,那目光自然就不能局限于JUnit自己了。通过引入JUnit Platform,JUnit 5不但可以运行新老版本的JUnit测试,甚至还可以运行别人家的单元测试,比如TestNG。套用一个现在流行的词,就叫降维打击。


图片出处:JUnit 5 - An Early Test Drive - Part 1

从上图可以看到,完整的JUnit 5平台从上至下分为四层:

  1. 面向developer的API,比如各种测试注解
  2. 特定于某一单元测试框架的测试引擎,比如JUnit 4,JUnit 5,TestNG
  3. 通用的测试引擎,是对第2层各种单元测试框架的抽象
  4. 面向IDE的启动器,用于调度和执行各个单元测试

前两层体现的是JUnit as a Tool,还属于Junit原本的范畴,而后两层体现的就是JUnit as a Platform,JUnit 5也借此实现了十年磨一剑的凤凰涅槃。有了JUnit as a Platform这把屠龙刀,相比于其他单元测试框架,可以说JUnit 5占据了技术制高点,可以预期,未来JUnit的版图将进一步扩大,将触角延伸至更多的测试领域。

特性

分析完JUnit 5架构上的调整,接下来再看一下除了Java 8的支持,JUnit 5具体包含了哪些值得一提的新特性。

  • 兼容性:为了最大程度的保证对JUnit 4以及更早版本的兼容性,JUnit 5启用了新的命名空间org.junit.jupiter.api.*,通过引入junit-vintage-engine模块,支持老的命名空间org.junit.*,并且新老版本可以共存于同一项目,独立运行互不影响。
  • 新的注解:
    • @DisplayName, @Tag注解:引入更灵活的基于字符串的@Tag注解,取代老的基于类的@Category注解,配以@DisplayName注解,让管理大量单元测试变得更容易。
    • @Nested注解:支持BDT(Behavior Driven Testing)风格的单元测试。
  • Parameterized Tests:批量生成参数化的单元测试,比如针对一个枚举类,为其每一个枚举值生成一个单元测试。
  • Dynamic Tests:程序化生成单元测试,比如动态从数据库中读取数据,生成单元测试。
  • Extention:JUnit 5提供了多种扩展方式,涉及测试周期的各个阶段,比如激活条件,参数解析,回调函数,异常处理等。通过实现特定接口,配合@ExtendWith注解,你很容易就可以重新定义一个单元测试的执行流程。

为了更好的理解这些特性,不妨去GitHub看一下JUnit官方的示例工程

Roadmap

根据最新的JUnit官方文档,JUnit 5将于今年第三季度的某一时刻发布,在此之前,还将发布若干个RC版本。目前最新的稳定版本是5.0.0-M4。

你,准备好了吗?

附录:JUnit 5和JUnit 4的快速对比



图片出处:JUnit 5 vs JUnit 4

参考