2. 概述

本文将深入探讨如何使用自定义测试运行器(Test Runner)来执行 JUnit 测试。简单来说,要指定自定义运行器,我们需要使用 @RunWith 注解。

3. 准备工作

首先在 pom.xml 中添加标准的 JUnit 依赖:

<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.12</version>  
</dependency>

4. 实现自定义运行器

下面通过示例展示如何编写自定义 Runner 并通过 @RunWith 运行它。

JUnit 运行器本质是继承自 JUnit 抽象 Runner 类的子类,负责通过反射机制执行 JUnit 测试

以下是 Runner 抽象方法的实现:

public class TestRunner extends Runner {

    private Class testClass;
    public TestRunner(Class testClass) {
        super();
        this.testClass = testClass;
    }

    @Override
    public Description getDescription() {
        return Description
          .createTestDescription(testClass, "My runner description");
    }

    @Override
    public void run(RunNotifier notifier) {
        System.out.println("running the tests from MyRunner: " + testClass);
        try {
            Object testObject = testClass.newInstance();
            for (Method method : testClass.getMethods()) {
                if (method.isAnnotationPresent(Test.class)) {
                    notifier.fireTestStarted(Description
                      .createTestDescription(testClass, method.getName()));
                    method.invoke(testObject);
                    notifier.fireTestFinished(Description
                      .createTestDescription(testClass, method.getName()));
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

关键点解析:

  • getDescription 方法继承自 Describable,返回包含测试信息的 Description 对象
  • run 方法通过反射调用目标测试方法
  • 构造函数必须接收 Class 参数(JUnit 规范要求)
  • RunNotifier 用于触发测试进度事件

在测试类中使用该运行器:

public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
}

@RunWith(TestRunner.class)
public class CalculatorTest {
    Calculator calculator = new Calculator();

    @Test
    public void testAddition() {
        System.out.println("in testAddition");
        assertEquals("addition", 8, calculator.add(5, 3));
    }
}

执行结果:

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.baeldung.junit.CalculatorTest
running the tests from MyRunner: class com.baeldung.junit.CalculatorTest
in testAddition
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.002 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

5. 专业运行器扩展

相比直接继承底层的 Runner 类,**更推荐继承其专业子类:ParentRunnerBlockJUnit4ClassRunner**。

ParentRunner:以层级方式执行测试
BlockJUnit4ClassRunner:具体实现类,适合覆盖特定方法

示例扩展 BlockJUnit4ClassRunner

public class BlockingTestRunner extends BlockJUnit4ClassRunner {
    public BlockingTestRunner(Class<?> klass) throws InitializationError {
        super(klass);
    }

    @Override
    protected Statement methodInvoker(FrameworkMethod method, Object test) {
        System.out.println("invoking: " + method.getName());
        return super.methodInvoker(method, test);
    }
}

使用 @RunWith(JUnit4.class) 会调用当前版本的默认 JUnit 4 运行器:

@RunWith(JUnit4.class)
public class CalculatorTest {
    Calculator calculator = new Calculator();

    @Test
    public void testAddition() {
        assertEquals("addition", 8, calculator.add(5, 3));
    }
}

6. 总结

JUnit 运行器具有高度可定制性,允许开发者:

  • 修改测试执行流程
  • 重构整个测试过程

⚠️ 踩坑提示:只需做少量修改时,优先查看 BlockJUnit4ClassRunner 的受保护方法

常用第三方运行器实现:

  • SpringJUnit4ClassRunner
  • MockitoJUnitRunner
  • HierarchicalContextRunner
  • Cucumber Runner

所有示例代码可在 GitHub 项目 中获取(Maven 项目,可直接导入运行)。


原始标题:Custom JUnit4 Test Runners