1. 概述

本文将介绍如何直接通过 Java 代码运行 JUnit 测试。这种能力在某些场景下非常实用,比如:

✅ 动态触发测试流程
✅ 嵌入式测试执行(如 CLI 工具、插件系统)
✅ 自定义测试调度器或 CI 扩展

如果你刚接触 JUnit,或者正准备升级到 JUnit 5,可以参考我们其他关于 JUnit 的系列教程


2. Maven 依赖

要支持 JUnit 4 和 JUnit 5 的程序化调用,需要引入以下核心依赖:

<dependencies>
    <!-- JUnit 5 执行引擎 -->
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <version>5.10.2</version>
        <scope>test</scope>
    </dependency>

    <!-- JUnit 平台启动器(核心 API) -->
    <dependency>
        <groupId>org.junit.platform</groupId>
        <artifactId>junit-platform-launcher</artifactId>
        <version>1.10.0</version>
    </dependency>

    <!-- JUnit 4 支持 -->
    <dependency> 
        <groupId>junit</groupId> 
        <artifactId>junit</artifactId> 
        <version>4.13.2</version> 
        <scope>test</scope> 
    </dependency>
</dependencies>

📌 最新版依赖可在 Maven Central 查找:

⚠️ 注意:junit-platform-launcher 不要设为 test 范围,否则主程序无法访问。


3. 运行 JUnit 4 测试

3.1. 测试用例准备

我们先定义两个简单的测试类作为演示:

public class FirstUnitTest {

    @Test
    public void whenThis_thenThat() {
        assertTrue(true);
    }

    @Test
    public void whenSomething_thenSomething() {
        assertTrue(true);
    }

    @Test
    public void whenSomethingElse_thenSomethingElse() {
        assertTrue(true);
    }
}
public class SecondUnitTest {

    @Test
    public void whenSomething_thenSomething() {
        assertTrue(true);
    }

    @Test
    public void whensomethingElse_thenSomethingElse() {
        assertTrue(true);
    }
}

这些类使用 JUnit 4 的 @Test 注解标记测试方法。其他注解如 @Before@After 也常用,但不在本文讨论范围。


3.2. 运行单个测试类

通过 JUnitCore 类即可从 Java 代码启动测试,搭配 TextListener 可输出结果到控制台:

JUnitCore junit = new JUnitCore();
junit.addListener(new TextListener(System.out));
junit.run(FirstUnitTest.class);

输出示例:

Running one test class:
..
Time: 0.019
OK (3 tests)

✅ 简单粗暴,适合快速验证。


3.3. 运行多个测试类

只需将多个类传入 run() 方法即可:

JUnitCore junit = new JUnitCore();
junit.addListener(new TextListener(System.out));

Result result = junit.run(
    FirstUnitTest.class, 
    SecondUnitTest.class);

resultReport(result);

结果通过 Result 对象获取,辅助方法如下:

public static void resultReport(Result result) {
    System.out.println("Finished. Result: Failures: " +
        result.getFailureCount() + ". Ignored: " +
        result.getIgnoreCount() + ". Tests run: " +
        result.getRunCount() + ". Time: " +
        result.getRunTime() + "ms.");
}

输出:

Finished. Result: Failures: 0. Ignored: 0. Tests run: 5. Time: 123ms.

3.4. 运行测试套件(Test Suite)

若想把多个测试类组合成一个逻辑单元,可使用 @RunWith(Suite.class) 定义测试套件:

@RunWith(Suite.class)
@Suite.SuiteClasses({
    FirstUnitTest.class,
    SecondUnitTest.class
})
public class MyTestSuite {
}

运行方式与普通类一致:

JUnitCore junit = new JUnitCore();
junit.addListener(new TextListener(System.out));
Result result = junit.run(MyTestSuite.class);
resultReport(result);

✅ 适合组织模块化测试集,结构清晰。


3.5. 重复执行测试

JUnit 4 提供了 RepeatedTest,可用于压力测试或验证随机性逻辑:

Test test = new JUnit4TestAdapter(FirstUnitTest.class);
RepeatedTest repeatedTest = new RepeatedTest(test, 5);

JUnitCore junit = new JUnitCore();
junit.addListener(new TextListener(System.out));
junit.run(repeatedTest);

📌 注意:需使用 JUnit4TestAdapter 包装测试类。

更进一步,还可以动态构建测试套件并设置不同重复次数:

TestSuite mySuite = new TestSuite();

mySuite.addTest(new RepeatedTest(new JUnit4TestAdapter(FirstUnitTest.class), 5));
mySuite.addTest(new RepeatedTest(new JUnit4TestAdapter(SecondUnitTest.class), 3));

JUnitCore junit = new JUnitCore();
junit.addListener(new TextListener(System.out));
junit.run(mySuite);

⚠️ ActiveTestSuite 已过时,建议使用标准 TestSuite


4. 运行 JUnit 5 测试

4.1. 测试用例准备

沿用之前的测试类,但需改为 JUnit 5 的注解和断言:

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class FirstUnitTest {
    @Test
    void whenThis_thenThat() {
        assertTrue(true);
    }
    // 其他方法略
}

差异点:

  • @Test 来自 org.junit.jupiter.api
  • 断言方法在 Assertions 类中
  • 方法可为 privatevoid,不限 public

4.2. 运行单个测试类

JUnit 5 使用 Launcher 模型,流程如下:

  1. 构建 LauncherDiscoveryRequest
  2. 创建 Launcher
  3. 发现测试计划(TestPlan
  4. 注册监听器
  5. 执行

示例代码:

public class RunJUnit5TestsFromJava {
    SummaryGeneratingListener listener = new SummaryGeneratingListener();

    public void runOne() {
        LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
            .selectors(selectClass(FirstUnitTest.class))
            .build();

        Launcher launcher = LauncherFactory.create();
        launcher.registerTestExecutionListeners(listener);
        launcher.execute(request);
    }
}

📌 SummaryGeneratingListener 是 JUnit 提供的便捷监听器,用于收集执行摘要。


4.3. 运行多个测试类

通过包扫描 + 类名过滤,轻松批量运行测试:

public void runAll() {
    LauncherDiscoveryRequest request = LauncherDiscoveryRequestBuilder.request()
        .selectors(selectPackage("com.example.junit5"))
        .filters(includeClassNamePatterns(".*Test"))
        .build();

    Launcher launcher = LauncherFactory.create();
    launcher.registerTestExecutionListeners(listener);
    launcher.execute(request);
}

关键点:

  • selectPackage():指定根包路径
  • includeClassNamePatterns():正则匹配类名,如 *Test*IT

✅ 这种方式非常适合自动化集成测试调度。


4.4. 获取测试输出

通过监听器获取执行结果:

public static void main(String[] args) {
    RunJUnit5TestsFromJava runner = new RunJUnit5TestsFromJava();
    runner.runAll();

    TestExecutionSummary summary = runner.listener.getSummary();
    summary.printTo(new PrintWriter(System.out));
}

输出示例:

Test run finished after 177 ms
[         7 containers found      ]
[         0 containers skipped    ]
[         7 containers started    ]
[         0 containers aborted    ]
[         7 containers successful ]
[         0 containers failed     ]
[        10 tests found           ]
[         0 tests skipped         ]
[        10 tests started         ]
[         0 tests aborted         ]
[        10 tests successful      ]
[         0 tests failed          ]

📌 TestExecutionSummary 提供了丰富的统计信息,可用于生成报告或触发后续逻辑。


5. 总结

本文系统介绍了如何从 Java 应用中以编程方式运行 JUnit 4 和 JUnit 5 测试,涵盖:

✅ 单类 / 多类执行
✅ 测试套件组织
✅ 重复测试(JUnit 4)
✅ 基于 Launcher 的现代模型(JUnit 5)

这些技巧在构建自定义测试工具链时非常有用,比如:

  • 内嵌测试面板(IDE 插件)
  • CLI 测试运行器
  • CI/CD 中的条件测试触发

所有示例代码均已上传至 GitHub:

踩坑提醒:
⚠️ JUnit 5 的 launcher 必须注册监听器才能拿到结果
⚠️ junit-platform-launcher 依赖不能放在 test scope
⚠️ 包扫描时注意类路径是否正确加载

掌握这些 API,你就拥有了“控制测试执行”的能力,不再是被动等待构建工具驱动测试了。


原始标题:Running JUnit Tests from a Java Application