Junit4的核心原理
这篇文章的目标是讲清楚Junit4是如何运行的,以及它内部的一些核心扩展点,下一篇文章会讲解Spring是如何与Junit集成的。
认识Junit4核心组件
认识Junit4的核心组件之前,我们需要知道如何debug junit4的源码。
首先引入依赖,注意,junit的scope并没有设置为test
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2<version>
</dependency>
然后编写一个测试用例类和一个入口类
@Slf4j
public class TestClass {
@Test
public void testOneEqualsOne() {
log.info("testOneEqualsOne running...");
Assert.assertEquals(1, 1);
}
}
public class JunitMain {
/**
* Junit源码调试入口
*/
public static void main(String[] args) {
Request request = Request.classes(TestClass.class);
JUnitCore jUnitCore = new JUnitCore();
jUnitCore.run(request);
}
}
然后就可以开始debug Junit的源码了,其实IDEA运行Junit4测试用例也是这个逻辑,看下面debug源码
debug之前,我先介绍一下Junit的核心组件,这样在debug过程中才能有的放矢。
-
JUnitCore
-
主要职责:作为 JUnit4 的主要入口点,提供了运行测试的高层接口。
-
核心方法:run(Class<?>... classes), run(Request request)
-
协作:
- 使用 Request 来创建适当的 Runner
- 创建 RunNotifier 来处理测试执行的通知
- 调用 Runner 的 run 方法来执行测试
-
-
Request
-
主要职责:封装如何运行测试的请求,是一个抽象类。
-
核心方法:getRunner()
-
子类:
- MemoizingRequest:缓存 Runner 的创建结果
- ClassRequest:表示运行单个测试类的请求
-
协作:
- 被 JUnitCore 使用来获取适当的 Runner
- 通常与 RunnerBuilder 配合使用来创建 Runner
-
-
Runner
-
主要职责:定义了运行测试和描述测试结构的抽象接口。
-
核心方法:run(RunNotifier), getDescription()
-
主要实现:
- ParentRunner:处理有子测试的测试类或测试套件
- BlockJUnit4ClassRunner:执行标准的 JUnit4 测试类
-
协作:
- 被 Request 创建
- 使用 RunNotifier 来通知测试执行状态
- 使用 Description 来描述测试结构
-
-
RunnerBuilder
-
主要职责:创建适当的 Runner 实例。
-
核心方法:runnerForClass(Class<?> testClass)
-
主要实现:
- AllDefaultPossibilitiesBuilder:尝试多种方式创建 Runner
- AnnotatedBuilder:处理 @RunWith 注解
- JUnit4Builder:创建默认的 JUnit4 Runner
-
协作:
- 被 Request 使用来创建 Runner
- AllDefaultPossibilitiesBuilder 使用其他具体的 Builder
-
-
RunNotifier
-
主要职责:在测试执行过程中发送事件通知。
-
核心方法:fireTestStarted(), fireTestFailure(), fireTestFinished() 等
-
协作:
- 被 Runner 使用来通知测试执行状态
- 管理多个 RunListener 实例
- 与 EachTestNotifier 配合,简化单个测试的通知
-
-
RunListener
-
主要职责:监听测试执行过程中的事件。
-
核心方法:testStarted(), testFailure(), testFinished() 等
-
协作:
- 被添加到 RunNotifier 中
- 接收来自 RunNotifier 的测试执行通知
-
-
Description
-
主要职责:描述测试的结构(类或方法)。
-
核心方法:createTestDescription(), createSuiteDescription()
-
协作:
- 被 Runner 使用来描述测试结构
- 被 RunNotifier 用于通知特定测试的状态
- 实现 Annotatable 接口,提供访问注解的能力
-
-
TestClass
-
主要职责:封装测试类的反射信息。
-
核心方法:getAnnotatedMethods(), getAnnotatedFields()
-
协作:
- 被 Runner(特别是 ParentRunner)使用来获取测试类的结构信息
- 管理 FrameworkMethod 和 FrameworkField 实例
-
-
FrameworkMethod 和 FrameworkField
-
主要职责:分别表示测试类中的方法和字段。
-
核心方法:
- FrameworkMethod: invokeExplosively()
- FrameworkField: get()
-
协作:
- 被 TestClass 使用来表示测试类的方法和字段
- 被 Runner 用于执行测试方法和访问测试字段
-
主要的协作流程如下:
- JUnitCore 作为入口点,接收测试类或 Request。
- Request(可能是 ClassRequest 或 MemoizingRequest)负责创建适当的 Runner。
- Runner(如 ParentRunner 或 BlockJUnit4ClassRunner)负责执行测试。
- RunnerBuilder 及其实现类用于创建特定的 Runner。
- Runner 使用 RunNotifier 来发送测试执行的事件通知。
- RunListener 监听这些事件通知。
- Description 用于描述测试结构,被 Runner 和 RunNotifier 使用。
- TestClass 封装测试类的结构信息,包括 FrameworkMethod 和 FrameworkField。
- ParentRunner 和 BlockJUnit4ClassRunner 使用 TestClass、FrameworkMethod 等来执行具体的测试逻辑。
一张图理解Junit4
其协作过程如下
- 用户通过JUnitCore运行测试类。
- JUnitCore创建一个ClassRequest。
- 通过RunnerBuilder创建适当的Runner(这里是BlockJUnit4ClassRunner)。
- 创建RunNotifier和RunListener。
- 调用Runner的run方法,开始执行测试。
- ParentRunner获取所有的测试方法(FrameworkMethods)。
- 对每个测试方法:
- 通知测试开始
- 执行测试方法
- 根据测试结果通知测试完成或失败
- 所有测试执行完毕后,通知测试运行结束。
- 返回测试结果给用户。