JUnit 5 单元测试完整笔记
一、JUnit 5 概述
JUnit 5 是 Java 生态中最主流的单元测试框架,提供丰富的注解、断言和扩展机制。
1.1 框架构成(三个子项目)
| 子项目 | 说明 |
|---|
| JUnit Platform | 在 JVM 上启动测试框架的基础平台,定义了 TestEngine API |
| JUnit Jupiter | 包含 JUnit 5 全新的编程模型和扩展机制 |
| JUnit Vintage | 提供对 JUnit 3 和 JUnit 4 测试的兼容运行支持 |
二、核心测试注解
JUnit 5 通过注解控制测试执行流程与行为。
| 注解 | 说明 | 使用场景 |
|---|
@Test | 标记一个方法为测试方法 | 基本单元测试 |
@BeforeEach | 在每个测试方法执行前运行 | 测试数据准备、对象初始化 |
@AfterEach | 在每个测试方法执行后运行 | 资源清理、状态重置 |
@BeforeAll | 在当前测试类的所有测试方法执行前运行一次(需为 static) | 类级别初始化(如建立数据库连接) |
@AfterAll | 在所有测试方法执行后运行一次(需为 static) | 类级别资源释放 |
@DisplayName | 为测试类或方法自定义易读的显示名称 | 提升测试报告的可读性 |
@Disabled | 临时禁用某个测试方法或类 | 跳过尚未实现或需要暂时忽略的测试 |
@ParameterizedTest | 声明参数化测试方法 | 使用多组数据驱动同一测试逻辑 |
@RepeatedTest | 重复执行指定次数的测试 | 验证方法稳定性或简单压力测试 |
@Timeout | 指定测试方法执行超时时间 | 性能验证、防止死循环 |
三、断言方法(Assertions)
JUnit 5 提供多种断言来验证测试结果。
| 方法 | 用途 |
|---|
assertEquals(expected, actual) | 断言期望值与实际值相等 |
assertTrue(condition) / assertFalse(condition) | 断言条件为真 / 假 |
assertThrows(ExceptionType.class, executable) | 断言执行代码会抛出指定异常 |
assertAll(executables...) | 组合多个断言,所有断言都会执行再报告失败(避免因一个失败而中断后续验证) |
四、参数化测试数据源
通过 @ParameterizedTest 结合数据源注解,用多组数据测试同一逻辑。
| 注解 | 说明 |
|---|
@ValueSource | 提供简单值数组(如 ints = {1, 2, 3}) |
@CsvSource | 提供 CSV 格式的字符串数据,支持多列参数 |
@MethodSource | 引用一个返回 Stream / Iterable 的静态方法提供测试数据 |
@EnumSource | 使用枚举类的所有值或指定子集作为测试数据 |
五、测试编写原则与最佳实践
5.1 好的测试实践(推荐)
✅ 示例代码
@Test
@DisplayName("当输入两个正数时应该返回正确的和")
void shouldReturnSumWhenAddingTwoPositiveNumbers() {
// Arrange - 准备测试数据
Calculator calculator = new Calculator();
// Act - 执行被测试的方法
int result = calculator.add(2, 3);
// Assert - 验证结果
assertEquals(5, result);
}
✅ 核心原则
- AAA 模式:Arrange(准备) → Act(执行) → Assert(断言)
- 命名清晰:测试方法名应具有描述性,表达“测什么”和“预期结果”
- 使用
@DisplayName:进一步提高测试报告可读性 - 单一职责:每个测试方法只验证一个功能点
- 覆盖边界条件与异常:不仅要测试正常路径,还要测试空值、零值、负数、越界等异常场景
5.2 应该避免的实践(反模式)
❌ 反面示例
@Test
void test1() {
// 测试多个不相关的功能
assertEquals(5, calculator.add(2, 3));
assertEquals(6, calculator.multiply(2, 3));
assertEquals("hello", stringUtils.reverse("olleh"));
}
❌ 错误做法
- 测试方法名无意义(如
test1、testAdd) - 一个测试方法中验证多个不相关的功能点
- 测试之间存在执行顺序或数据依赖
- 忽略边界条件与异常分支
- 测试代码逻辑过于复杂或包含大量重复代码
六、常用测试类型
| 类型 | 说明 |
|---|
| 基本功能测试 | 验证方法在正常输入下的预期行为 |
| 边界值测试 | 测试输入为 null、空字符串、0、最大值、最小值等边界情况 |
| 异常测试 | 使用 assertThrows 验证错误输入时是否正确抛出异常 |
| 参数化测试 | 使用多组数据驱动同一测试逻辑,减少重复代码 |
| 性能测试 | 结合 @Timeout 或性能测试框架验证执行效率 |
七、测试编写注意事项
- 测试独立性:每个测试应能单独运行,不依赖其他测试的执行结果或执行顺序。
- 数据自包含:测试数据应在测试方法内部准备,避免使用共享可变全局状态。
- 断言消息:为关键断言添加描述性消息,便于快速定位失败原因。
- 资源清理:在
@AfterEach 或 @AfterAll 中释放资源(如文件流、数据库连接)。 - 代码简洁:保持测试代码易读、易维护,必要时可提取私有辅助方法。
八、测试覆盖率目标
| 指标 | 建议值 |
|---|
| 整体代码覆盖率 | ≥ 80% |
| 核心业务逻辑 | 重点覆盖,尽量达到 90% 以上 |
| 公共方法 | 所有 public 方法均应有对应测试 |
| 异常分支与边界条件 | 必须包含相关测试用例 |
| 覆盖率工具 | 使用 JaCoCo 等工具生成报告并持续监控 |
笔记总结完毕。 按照以上结构和内容复习,即可系统掌握 JUnit 5 的使用方法与单元测试最佳实践。
评论