第1章 JUnit概览:发展历史、核心功能与TestNG对比
做测试的同学,肯定绕不开 JUnit。我入行那会儿,JUnit 3 还是主流,每个测试类要继承 TestCase,方法名必须以 test 开头,写起来挺麻烦的。后来 JUnit 4 出来了,注解驱动,感觉突然解放了双手。再到现在的 JUnit 5,整个架构都重构了一遍,很多设计理念很值得琢磨。
本章我带大家快速过一遍 JUnit 的来龙去脉,同时和 TestNG 做个对比。这样你心里有底,知道什么时候该用什么。
1.1 JUnit 的发展历史
JUnit 到现在已经走过了快 30 年,我整理了几个关键节点:
| 版本 | 发布时间 | 核心变化 |
|---|---|---|
| JUnit 3 | 1999 | 继承 TestCase,方法命名约定,断言方法在父类 |
| JUnit 4 | 2006 | 注解驱动(@Test、@Before等),支持参数化、套件 |
| JUnit 5 | 2017 | 模块化架构(平台+Jupiter+Vintage),扩展模型升级,动态测试 |
重点说说 JUnit 5。它不再是一个大 jar 包,而是拆成了三个子项目:
- JUnit Platform — 在 JVM 上启动测试框架的基础。IDE 和构建工具(Maven、Gradle)都通过它来发现和执行测试。
- JUnit Jupiter — 新的编程模型和扩展 API。我们平时写的 @Test、@ParameterizedTest 都在这里。
- JUnit Vintage — 提供兼容层,让 JUnit 3/4 的测试用例也能在 JUnit 5 平台上跑。
这个设计特别聪明,你想想看——公司项目里老代码一堆 JUnit 4 测试,直接升级到 JUnit 5,加个 Vintage 依赖就能全部跑通,不用重写。我之前帮一个团队迁移过,当天就搞定了。
1.2 JUnit 5 核心功能一览
JUnit 5 的功能很多,我觉得最常用的有这么几个:
- 注解驱动 — @Test、@BeforeEach、@AfterEach、@BeforeAll、@AfterAll、@DisplayName 等。写测试像写故事一样清晰。
- 断言 API — 既有经典的
assertEquals、assertTrue,也有 JUnit 5 新增的assertAll、assertThrows和assertTimeout。我个人特别喜欢assertAll,它可以一次性报告多个失败点,不用卡在第一个错就停。 - 参数化测试 — 这是本课程的重点。用 @ParameterizedTest 配合 @ValueSource、@CsvSource、@MethodSource 等,一条测试方法跑多组数据。我曾在项目中用它来测试一个日期校验工具,12 组边界值,一行代码搞定。
- 动态测试 — @TestFactory 返回动态生成的测试用例,适合数据驱动场景。
- 标签与过滤 — @Tag 可以按环境(dev、staging、prod)或类型(fast、slow)分组,构建工具里指定包含或排除。
- 扩展模型 — 以前叫 Runner,现在用 Extension。可以实现自定义条件执行、参数注入、异常处理等。
看个简单的参数化测试例子,感受一下:
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5})
void testIsPositive(int number) {
assertTrue(number > 0);
}
5 个输入,5 个独立测试用例。如果第二个错了,不影响其他 4 个执行——这在调试时特别有用。
1.3 JUnit vs TestNG
JUnit 和 TestNG 就像咖啡和茶,各有拥趸。我从实际使用角度做个对比,帮你快速决策。
| 维度 | JUnit 5 | TestNG |
|---|---|---|
| 注解风格 | @Test, @BeforeEach, @AfterAll | @Test, @BeforeMethod, @AfterSuite |
| 参数化 | @ParameterizedTest + @ValueSource / @CsvSource / @MethodSource | @Parameters + XML / DataProvider |
| 依赖测试 | 官方没有,但可以用 @Order 控制顺序 | @Test(dependsOnMethods = "...") 原生支持 |
| 分组/套件 | @Tag + @Suite | @Groups + XML 配置 |
| 并行执行 | 通过配置启用,@Execution(CONCURRENT) | XML 中设置 parallel,更成熟 |
| 扩展性 | Extension API,非常灵活 | Listener / 拦截器 |
| 工具生态 | Maven/Gradle 默认集成,IDEA、Eclipse 原生支持 | 同样主流,但 Jenkins 等 CI 默认也支持 |
选型建议:
- 如果项目主要是单元测试,团队习惯 Spring Boot(它自带 JUnit 5),老老实实用 JUnit 5 就好。
- 如果需要复杂的测试依赖(比如 A 测试必须在 B 测试之后执行),或者大量配置 XML 套件,TestNG 会更顺手。
- 我曾经在一个金融项目中遇到需要做数据驱动的回归测试,有几十个测试类互相依赖——当时选了 TestNG 的 dependsOnGroups,管理起来确实比 JUnit 的 @Order 容易。
不过说实话,现在 JUnit 5 的扩展性已经很强了,大部分场景都能 cover。
1.4 本章知识体系速览
下面这张图把 JUnit 的核心脉络画出来了。你花 30 秒看清整体,后面学起来就不容易迷路。
这个结构图里,参数化测试是 JUnit Jupiter 下注解驱动的核心能力之一。我们后面会用大量案例来拆解它。
1.5 从小处开始写
最后说个实用建议。我刚接触 JUnit 5 时,总想一口气学完所有功能,结果效率很低。后来我发现:先写一个最简单的 @Test,再慢慢加入 @BeforeEach、@ParameterizedTest,逐步叠加。就像搭积木,每个新特性都拿一个真实业务场景去练手。