JUnit 4
除了 Hamcrest,JUnit 4没有任何外部依赖,其所有的功能都被打包在一个构件(artifact)中。这完全违反了单一职责原则,它被提供给开发者、IDE、构建工具、其他测试框架、其他扩展等使用,不同的使用者,依赖的都是一个同样的构件。
而在这其中,只有开发者能——或者说曾经能——以干净的方法来使用它。他们通常只需要 JUnit 的公共 API,不需要管其他的。非常好。
但生态圈中的其他成分则不是这样使用 JUnit:测试框架、扩展,特别是 IDE 和构建工具的开发者,他们需要深入到 JUnit 的深处,到它的细枝末节:非 public 的类、内部 API,甚至 private 字段。它们的正常工作极大地依赖于 JUnit 的实现细节。这使得 JUnit 维护团队不能轻易地修改框架的这些内部实现,因此团队的开发进度受到了很大的影响。
当然,这些工具的开发者们也并非有意为之。为了实现那些我们十分喜爱的特性,他们不得不使用内部的 API,因为 JUnit 4 并没有提供相应的 API:一个强大到足以满足工具开发者们需求的 API。
Junit Lambda 团队开始着手于 JUnit 5 的开发,希望能让这一切变得明朗起来。
JUnit 5
分离的关注点
退一步想,我们不难辨识出,这里至少有两个不同的关注点需要分离:
1、一个支持测试代码撰写的 API
2、一个识别测试、运行测试的机制
再仔细思考一下第二点,我们可能会问,“哪些测试?”这个当然是指 Junit 测试。“我知道,但具体是哪些版本的测试呢?”呃…“还有,具体是指什么类型的测试?”好吧,你让我给你……“只能跑那些老版本的 @Test 注解的测试么?有没有其他新的方法来运行测试呢?……”行行行,都给我闭嘴!听我讲着。
为了进一步将待识别测试的类型 与 实际运行它们 这两个关注点解耦,上面的第二点需要细分:
1、一个支持测试代码撰写的 API
2、一个识别测试、运行测试的机制
· 一个识别、运行特定类型(比如,JUnit 5测试的机制)
· 另一套协调上述机制的机制
· 上两者之间的 API
JUnit 5 的重新的组织
识别出这两个关注点以后,“作为平台的 JUnit ”(用于运行我们的测试)和“作为工具的 JUnit ”(用于撰写我们的测试)这两个概念的分离清晰了。为了完成这个彻底的分离,JUnit 团队决定将 JUnit 5 分成三个子项目:
JUnit Jupiter
包含了我们用于撰写测试的 API(关注点1),以及一个能理解测试代码的引擎(关注点2.1)。
JUnit Platform
提供了一套统一的 API 以运行测试,及基于 API 之上的一套工具(关注点2.2和2.3)。
JUnit Vintage
提供了一套引擎,用以在 JUnit 5 中运行 JUnit 3 和 JUnit 4 的测试(关注点2.1)。
架构与体系
JUnit 5 的架构体系完全是遵循这个关注点分离思想的产物:
junit-jupiter-api(1)
开发者用于撰写测试的 API,包含了我们在 JUnit 5 的基础知识 一节中所提及的所有注解、断言等。
junit-platgorm-engine(2.3)
包含了一套所有测试引擎都必须实现的 API。这样,不同的测试引擎之间可以通过统一的接口被调用。引擎可以跑正常的 JUnit 测试,但也可以实现不同的引擎用以执行其他框架写成的测试,如 TestNG 、 Spock 、 Cucumber 等。
junit-jupiter-engine(2.1)
junit-platform-engineAPI 的一个实现,专门用于执行 JUnit 5 撰写的测试。
junit-vintage-engine(2.1)
junit-platform-engineAPI 的一个实现,专门用于执行 JUnit 3 或 JUnit 4 撰写的测试。过去,JUnit 4 的构件 junit-4.12 充当了两个角色:它既是开发人员用于实现测试的 API,又包含了用以执行测试的核心组件。这个引擎,可以认为是低版本的 JUnit 3/4 与 JUnit 5 之间的一个适配器。
junit-platform-launcher(2.2)
这部分使用了一个服务加载器 ServiceLoader 来发现测试引擎,并协调不同实现之间的执行。它提供了一个 API 给 IDE 和构建工具,使得它们能够与测试执行过程交互,比如,运行单个的测试、搜集测试结果并展示等。