JUnit 4 使用非常简单,我们直接分析其工作原理。

1. 工作原理

在 JUnit 初始化时,会调用 RunnerBuilder 将测试类 testClass 包装成 Runner,最后调用 Runner#run 方法执行单元测试。不用说,你也猜到,肯定是先扫描该类上有 @Test 注解的方法,然后调用方法的反射一一执行。 JUnit 源码解析 - 图1总结:JUnit 可以分为两个阶段。

  1. 初始化阶段:调用 RunnerBuilder 构建 Runner,本质是扫描有 @Test 注解的方法包装成 FrameworkMethod。
  2. 执行阶段:调用 Runner#run 时,通过方法的反射执行。

    2. 源码分析

    2.1 RunnerBuilder

    org.junit.internal.builders 包下,JUnit 对外提供 AllDefaultPossibilitiesBuilder 作为构建 Runner 的方式,默认提供了以下 5 种方式。
    1. AllDefaultPossibilitiesBuilder
    2. -> IgnoredBuilder // 1. @Ignored,对应IgnoredClassRunner
    3. -> AnnotatedBuilder // 2. @RunWith,对应RunWith.value值
    4. -> SuiteMethodBuilder // 3. 测试类有suit方法,对应SuiteMethod
    5. -> JUnit3Builder // 4. JUnit 3(TestCase),对应JUnit38ClassRunner
    6. -> JUnit4Builder // 5. JUnit 4(@Test),对应BlockJUnit4ClassRunner

    2.2 Runner

    Runner 是 JUnit 的核心,它封装了一个测试类中所有的测试方法,如 BlockJUnit4ClassRunner,或者是多个 Runner,如 Suite。上述 5 种 Runner,我们会重点介绍一下 AnnotatedBuilder 和 JUnit4Builder。

    2.2.1 JUnit38ClassRunner

    JUnit38ClassRunner 用于处理 TestCase 测试类,可以参考《JUnit之TestCase和TestSuite详解》。
    使用 JUnit38ClassRunner 时,有两个要求(具体代码见 TestSuite#isTestMethod):
  • 一是测试类需要继承 TestCase;
  • 二是测试方法名以 test 开头。
    1. public class MyTestCase extends TestCase {
    2. public void testMethod1() {
    3. }
    4. }

    2.2.2 AnnotatedBuilder

    AnnotatedBuilder 处理 @RunWith 注解,具有很好的扩展性,比如 SpringRunner 扩展。如果测试类上标注有 @RunWith 注解,AnnotatedBuilder#runnerForClass 方法会读取该标注的 Runner 类处理该测试类。
    1. @RunWith(SpringRunner.class)
    2. public class MyTest {
    3. }

    2.2.3 BlockJUnit4ClassRunner

    通常,如果测试类没有 @RunWith 注解,基本上就是 BlockJUnit4ClassRunner 来处理的。BlockJUnit4ClassRunner 其实也很简单:
  1. 初始化阶段:调用 scanAnnotatedMembers 方法扫描该没测试类的 @BeforeClass、@AfterClass、@Before、@After、@Test、@Ignore 注解。
  2. 执行阶段:将所有标注 @Test 的方法包装成 InvokeMethod,通过反射执行。调用链如下:

    1. ParentRunner#run
    2. -> classBlock // 创建 Class Statement
    3. -> childrenInvoker
    4. -> withBeforeClasses // @BeforeClass
    5. -> withAfterClasses // @AfterClass
    6. -> Statement#evaluate
    7. -> runChildren
    8. -> getChildren // @Test
    9. -> BlockJUnit4ClassRunner#runChild
    10. BlockJUnit4ClassRunner#runChild
    11. -> methodBlock // 创建 Method Statement
    12. -> methodInvoker
    13. -> withBefores // @Before
    14. -> withAfters // @After
    15. -> Statement#evaluate
    16. -> InvokeMethod#evaluate // 反射执行

    3. 总结时刻

    推荐阅读

  3. JUnit 5 新特性》IBM

  4. 有赞测试 有赞单元测试实践

每天用心记录一点点。内容也许不重要,但习惯很重要!