JUnit5 的说明和使用
JUnit5 的说明和使用
JUnit5 是Java领域最主流的单元测试框架,相比JUnit4 有更简洁的语法、更强大的功能(如参数化测试、动态测试),且完全兼容Java 8+的新特性(如Lambda表达式)。
一、JUnit 5 核心组成
JUnit 5 由三个核心模块组成,无需刻意区分,只需引入依赖即可自动关联:
1.JUnit Platform:测试运行平台,负责启动测试框架、运行测试用例
2.JUnit Jupiter:核心模块,包含新的测试注解(如Test)、测试引擎等
3.JUnit Vintage:兼容模块,用于运行JUnit 3/4 的老测试用例
二、环境准备(以Spring Boot 项目为例)
1.引入依赖
如果是Spring Boot项目,只需在pom.xml中添加spring-boot-starter-test依赖(已包含JUnit 5)
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope> <!-- 仅在测试环境生效 -->
</dependency>
非Spring Boot 项目需手动引入 JUnit 5 依赖:
<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId><version>5.9.2</version> <!-- 版本号可按需更新 --><scope>test</scope>
</dependency>
<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-engine</artifactId><version>5.9.2</version><scope>test</scope>
</dependency>
2.测试类位置
在Maven/Gradle项目中,测试类默认放在src/test/java目录下,包结构建议与被测试类保持一致(便于管理)
三、JUnit 5基础注解
JUnit 5用注解标识测试方法、生命周期等,常用注解如下:
| 注解 | 作用 |
|---|---|
| @Test | 标识一个测试方法(无需 static 修饰,JUnit 4 需 static) |
| @BeforeEach | 每个测试方法执行前运行(如初始化资源) |
| @AfterEach | 每个测试方法执行后运行(如释放资源) |
| @BeforeAll | 所有测试方法执行前运行一次(需修饰 static 方法,如初始化数据库连接) |
| @AfterAll | 所有测试方法执行后运行一次(需修饰 static 方法,如关闭数据库连接) |
| @Disabled | 禁用当前测试方法(暂时不执行) |
| @DisplayName | 为测试类或方法设置显示名称(便于在测试报告中识别) |
四、入门案例
假设现在有一个工具类 Calculator,需要测试其加法和减法逻辑
1.被测试类(业务代码)
// src/main/java/com/example/demo/util/Calculator.java
package com.example.demo.util;public class Calculator {// 加法public int add(int a, int b) {return a + b;}// 减法public int subtract(int a, int b) {return a - b;}
}
2.测试类(测试代码)
// src/test/java/com/example/demo/util/CalculatorTest.java
package com.example.demo.util;import org.junit.jupiter.api.*;import static org.junit.jupiter.api.Assertions.*; // 断言工具类@DisplayName("计算器测试类") // 测试类的显示名称
class CalculatorTest {private Calculator calculator;// 所有测试方法执行前初始化一次(static 方法)@BeforeAllstatic void beforeAll() {System.out.println("=== 开始所有测试 ===");}// 每个测试方法执行前初始化@BeforeEachvoid setUp() {calculator = new Calculator(); // 初始化计算器对象System.out.println("--- 开始单个测试 ---");}// 测试加法@Test@DisplayName("测试加法:1 + 2 = 3") // 测试方法的显示名称void testAdd() {// 调用被测试方法int result = calculator.add(1, 2);// 断言:验证结果是否符合预期(常用断言方法见下文)assertEquals(3, result, "1 + 2 应该等于 3");}// 测试减法@Test@DisplayName("测试减法:5 - 3 = 2")void testSubtract() {int result = calculator.subtract(5, 3);assertEquals(2, result); // 简化写法,不写错误提示}// 禁用此测试(暂时不执行)@Test@Disabled("暂时不测试乘法")void testMultiply() {// 未实现的测试逻辑}// 每个测试方法执行后清理@AfterEachvoid tearDown() {System.out.println("--- 单个测试结束 ---");}// 所有测试方法执行后清理(static 方法)@AfterAllstatic void afterAll() {System.out.println("=== 所有测试结束 ===");}
}
3.运行测试
idea中运行:在测试类或测试方法上右键,选择 run 运行
运行结果:控制台会显示测试是否通过,若失败会提示具体原因(如断言错误信息)
上述代码测试结果输出:
=== 开始所有测试 ===
--- 开始单个测试 ---
--- 单个测试结束 ---
--- 开始单个测试 ---
--- 单个测试结束 ---
=== 所有测试结束 ===
五、常用断言方法(验证结果)
JUnit 5 提供 Assertions 工具类,包含多种断言方法,用于验证测试结果是否符合预期:
| 断言方法 | 作用 | 示例 |
|---|---|---|
| assertEquals(a, b) | 验证 a 等于 b | assertEquals(3, 1+2) |
| assertNotEquals(a, b) | 验证 a 不等于 b | assertNotEquals(2, 1+2) |
| assertTrue(condition) | 验证条件为 true | assertTrue(1 < 2) |
| assertFalse(condition) | 验证条件为 false | assertFalse(1 > 2) |
| assertNull(obj) | 验证对象为 null | assertNull(null) |
| assertNotNull(obj) | 验证对象不为 null | assertNotNull(new Object()) |
| assertThrows(type, task) | 验证执行 task 会抛出 type 异常 | 见下文示例 |
断言异常的实例:
如果无法预期会抛出异常(如参数不合法时),可用 assertThrows 验证:
@Test
@DisplayName("测试除法:除数为0时抛出异常")
void testDivide() {// 定义一个除法方法(假设Calculator有divide方法)Calculator calculator = new Calculator();// 验证调用divide(1, 0)会抛出ArithmeticExceptionassertThrows(ArithmeticException.class, () -> calculator.divide(1, 0));
}
六、进阶功能:参数化测试(减少重复代码)
当需要用多组数据测试同一个方法时(如测试加法的不同输入),参数化测试可避免编写重发代码
步骤:
1.用 @ParameterizedTest 替代 @Test 标识测试方法
2.用数据源注解(如 @ValueSource)提供测试数据
实例:测试加法的多组数据
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.junit.jupiter.params.provider.CsvSource;// 参数化测试:多组数据测试加法
@ParameterizedTest
@DisplayName("参数化测试:加法多组数据")
// 格式:a, b, 预期结果(CSV格式)
@CsvSource({"1, 2, 3", // 1+2=3"0, 0, 0", // 0+0=0"-1, 1, 0", // -1+1=0"5, -3, 2" // 5+(-3)=2
})
void testAddWithParams(int a, int b, int expected) {int result = calculator.add(a, b);assertEquals(expected, result);
}
常用数据源注解:
@ValueSource(ints = {1,2,3}):单参数的数组(支持 int、String等类型)
@CsvSource({"a,b","c,d"}):多参数的CSV格式(逗号分隔)
@MethodSource("methodName"):从指定方法获取数据(适合复杂数据)
七、注意事项
1.测试方法规范:
方法名建议以testXxx开头(如testAdd),清晰表达测试目标
测试方法无返回值(void),且不能有参数(参数化测试除外)
2.测试独立性:
每个测试方法应独立运行,不依赖其他测试的结果(@BeforeEach 确保每次测试都是新的初始化)
3.断言明确:
断言时尽量添加错误提示(如 assertEquals(3, result, “1+2计算错误”)),方便排查问题
4.先写测试再写业务代码?
推荐“测试驱动开发(TDD)”:先写测试用例(明确预期结果),再实现业务代码,直到测试通过
总结
JUnit 5 核心流程:
1.引入依赖
2.创建测试类(与被测试类同包)
3.用 @Test 标识测试方法
4.用 Assertions 断言结果
5.运行测试验证逻辑
