Junit4的核心原理

72 阅读4分钟

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源码

image.png

image.png

debug之前,我先介绍一下Junit的核心组件,这样在debug过程中才能有的放矢。

  1. JUnitCore

    • 主要职责:作为 JUnit4 的主要入口点,提供了运行测试的高层接口。

    • 核心方法:run(Class<?>... classes), run(Request request)

    • 协作:

      • 使用 Request 来创建适当的 Runner
      • 创建 RunNotifier 来处理测试执行的通知
      • 调用 Runner 的 run 方法来执行测试
  2. Request

    • 主要职责:封装如何运行测试的请求,是一个抽象类。

    • 核心方法:getRunner()

    • 子类:

      • MemoizingRequest:缓存 Runner 的创建结果
      • ClassRequest:表示运行单个测试类的请求
    • 协作:

      • 被 JUnitCore 使用来获取适当的 Runner
      • 通常与 RunnerBuilder 配合使用来创建 Runner
  3. Runner

    • 主要职责:定义了运行测试和描述测试结构的抽象接口。

    • 核心方法:run(RunNotifier), getDescription()

    • 主要实现:

      • ParentRunner:处理有子测试的测试类或测试套件
      • BlockJUnit4ClassRunner:执行标准的 JUnit4 测试类
    • 协作:

      • 被 Request 创建
      • 使用 RunNotifier 来通知测试执行状态
      • 使用 Description 来描述测试结构
  4. RunnerBuilder

    • 主要职责:创建适当的 Runner 实例。

    • 核心方法:runnerForClass(Class<?> testClass)

    • 主要实现:

      • AllDefaultPossibilitiesBuilder:尝试多种方式创建 Runner
      • AnnotatedBuilder:处理 @RunWith 注解
      • JUnit4Builder:创建默认的 JUnit4 Runner
    • 协作:

      • 被 Request 使用来创建 Runner
      • AllDefaultPossibilitiesBuilder 使用其他具体的 Builder
  5. RunNotifier

    • 主要职责:在测试执行过程中发送事件通知。

    • 核心方法:fireTestStarted(), fireTestFailure(), fireTestFinished() 等

    • 协作:

      • 被 Runner 使用来通知测试执行状态
      • 管理多个 RunListener 实例
      • 与 EachTestNotifier 配合,简化单个测试的通知
  6. RunListener

    • 主要职责:监听测试执行过程中的事件。

    • 核心方法:testStarted(), testFailure(), testFinished() 等

    • 协作:

      • 被添加到 RunNotifier 中
      • 接收来自 RunNotifier 的测试执行通知
  7. Description

    • 主要职责:描述测试的结构(类或方法)。

    • 核心方法:createTestDescription(), createSuiteDescription()

    • 协作:

      • 被 Runner 使用来描述测试结构
      • 被 RunNotifier 用于通知特定测试的状态
      • 实现 Annotatable 接口,提供访问注解的能力
  8. TestClass

    • 主要职责:封装测试类的反射信息。

    • 核心方法:getAnnotatedMethods(), getAnnotatedFields()

    • 协作:

      • 被 Runner(特别是 ParentRunner)使用来获取测试类的结构信息
      • 管理 FrameworkMethod 和 FrameworkField 实例
  9. FrameworkMethod 和 FrameworkField

    • 主要职责:分别表示测试类中的方法和字段。

    • 核心方法:

      • FrameworkMethod: invokeExplosively()
      • FrameworkField: get()
    • 协作:

      • 被 TestClass 使用来表示测试类的方法和字段
      • 被 Runner 用于执行测试方法和访问测试字段

主要的协作流程如下:

  1. JUnitCore 作为入口点,接收测试类或 Request。
  2. Request(可能是 ClassRequest 或 MemoizingRequest)负责创建适当的 Runner。
  3. Runner(如 ParentRunner 或 BlockJUnit4ClassRunner)负责执行测试。
  4. RunnerBuilder 及其实现类用于创建特定的 Runner。
  5. Runner 使用 RunNotifier 来发送测试执行的事件通知。
  6. RunListener 监听这些事件通知。
  7. Description 用于描述测试结构,被 Runner 和 RunNotifier 使用。
  8. TestClass 封装测试类的结构信息,包括 FrameworkMethod 和 FrameworkField。
  9. ParentRunner 和 BlockJUnit4ClassRunner 使用 TestClass、FrameworkMethod 等来执行具体的测试逻辑。

一张图理解Junit4

image.png

其协作过程如下

image.png

  • 用户通过JUnitCore运行测试类。
  • JUnitCore创建一个ClassRequest。
  • 通过RunnerBuilder创建适当的Runner(这里是BlockJUnit4ClassRunner)。
  • 创建RunNotifier和RunListener。
  • 调用Runner的run方法,开始执行测试。
  • ParentRunner获取所有的测试方法(FrameworkMethods)。
  • 对每个测试方法:
    • 通知测试开始
    • 执行测试方法
  • 根据测试结果通知测试完成或失败
  • 所有测试执行完毕后,通知测试运行结束。
  • 返回测试结果给用户。