文章目录
- [一、JUnit5 简介与架构变化](#一、JUnit5 简介与架构变化)
-
- [1. JUnit5 的诞生背景](#1. JUnit5 的诞生背景)
- [2. JUnit5 的三大核心模块](#2. JUnit5 的三大核心模块)
-
- [(1)JUnit Platform ------ 测试执行平台](#(1)JUnit Platform —— 测试执行平台)
- [(2)JUnit Jupiter ------ 新一代测试编程模型](#(2)JUnit Jupiter —— 新一代测试编程模型)
- [(3)JUnit Vintage ------ 对旧版本的兼容支持](#(3)JUnit Vintage —— 对旧版本的兼容支持)
- [3. JUnit5 与 JUnit4 的关键区别](#3. JUnit5 与 JUnit4 的关键区别)
- [4. Spring Boot 中的 JUnit5 支持](#4. Spring Boot 中的 JUnit5 支持)
- [5. 架构图解(逻辑示意)](#5. 架构图解(逻辑示意))
- [6. 总结](#6. 总结)
- [二、JUnit5 常用注解详解(附完整代码案例)](#二、JUnit5 常用注解详解(附完整代码案例))
-
- [1. `@Test` ------ 标记测试方法](#1.
@Test—— 标记测试方法) - [2. `@DisplayName` ------ 设置测试名称(可读性增强)](#2.
@DisplayName—— 设置测试名称(可读性增强)) - [3. `@BeforeEach` ------ 每个测试前执行](#3.
@BeforeEach—— 每个测试前执行) - [4. `@AfterEach` ------ 每个测试后执行](#4.
@AfterEach—— 每个测试后执行) - [5. `@BeforeAll` ------ 所有测试前执行(静态方法)](#5.
@BeforeAll—— 所有测试前执行(静态方法)) - [6. `@AfterAll` ------ 所有测试后执行(静态方法)](#6.
@AfterAll—— 所有测试后执行(静态方法)) - [7. `@ParameterizedTest` + `@ValueSource` / `@CsvFileSource` ------ 参数化测试](#7.
@ParameterizedTest+@ValueSource/@CsvFileSource—— 参数化测试) -
-
- [示例 1:使用 `@ValueSource`](#示例 1:使用
@ValueSource) - [示例 2:使用 `@CsvFileSource`](#示例 2:使用
@CsvFileSource)
- [示例 1:使用 `@ValueSource`](#示例 1:使用
-
- [8. `@RepeatedTest` ------ 重复执行测试](#8.
@RepeatedTest—— 重复执行测试) - [9. `@Disabled` ------ 跳过测试](#9.
@Disabled—— 跳过测试) - [10. `@Timeout` ------ 设置超时时间](#10.
@Timeout—— 设置超时时间) - [11. `@ExtendWith` ------ 扩展测试功能](#11.
@ExtendWith—— 扩展测试功能)
- [1. `@Test` ------ 标记测试方法](#1.
- [三、Spring Boot 中的 JUnit5 实践](#三、Spring Boot 中的 JUnit5 实践)
- 四、总结
- 五、写在最后
前言
在现代软件开发中,单元测试 是保证代码质量、提升开发效率和降低维护成本的重要手段。随着 Spring Boot 2.2+ 版本开始默认引入 JUnit5 作为单元测试框架,掌握 JUnit5 的使用已成为 Java 开发者的必备技能。
本文将结合实际截图内容,全面介绍 JUnit5 的基本概念、核心架构,并通过丰富的代码示例详细讲解其常用的测试注解,帮助你快速上手并高效编写高质量的单元测试。
一、JUnit5 简介与架构变化
JUnit5 是 Java 社区中最主流的单元测试框架 JUnit 的第五代版本,标志着该框架进入了一个全新的发展阶段。它不仅在功能上实现了重大升级,更在设计理念、模块化结构和扩展能力方面进行了彻底重构,旨在为现代 Java 应用(尤其是基于 Java 8+ 和 Spring Boot 的项目)提供更强大、更灵活、更现代化的测试支持。
1. JUnit5 的诞生背景
在 JUnit5 之前,JUnit4 已经广泛使用多年。然而,随着 Java 平台的发展(特别是 Java 8 引入 Lambda 表达式、函数式接口等新特性),以及开发模式向微服务、容器化、持续集成/持续交付(CI/CD)演进,JUnit4 在以下几个方面逐渐显现出局限性:
- 语法老旧:不支持 Java 8+ 的新特性,如 Lambda 表达式。
- 扩展机制复杂 :JUnit4 的
@Rule和@ClassRule扩展方式不够灵活,学习成本高。 - 模块耦合度高:测试引擎、API 和运行平台紧密耦合,难以扩展或与其他测试框架共存。
- 功能不足:缺乏对参数化测试、动态测试、嵌套测试等现代测试需求的良好支持。
为解决这些问题,JUnit 团队从 2015 年起启动了 JUnit Lambda 项目,最终于 2017 年正式发布 JUnit5,彻底重构了整个测试框架体系。
2. JUnit5 的三大核心模块
JUnit5 采用"模块化设计 ",将整个框架拆分为三个独立但协同工作的子项目,构成了所谓的"JUnit Platform"生态系统。这种分层架构极大地提升了灵活性和可扩展性。
(1)JUnit Platform ------ 测试执行平台
- 定位:底层运行平台,是所有测试框架在 JVM 上运行的基础。
- 功能 :
- 提供一个稳定的 API 来启动测试。
- 支持多种测试引擎(TestEngine)在同一个环境中运行。
- 集成到构建工具(如 Maven、Gradle)和 IDE(如 IntelliJ IDEA、Eclipse)中。
- 典型实现 :
junit-platform-launcher:提供标准的测试发现与执行入口。- 允许第三方框架(如 Spock、TestNG)通过实现
TestEngine接口接入 JUnit Platform。
📌 举例:你可以在同一个项目中同时运行 JUnit5、JUnit4 和 Spock 测试,只要它们都注册了对应的
TestEngine。
(2)JUnit Jupiter ------ 新一代测试编程模型
-
定位 :JUnit5 的核心 API 和编程模型,开发者日常使用的主要部分。
-
功能 :
- 提供全新的注解(如
@Test,@ParameterizedTest,@DisplayName等)。 - 支持 Java 8+ 特性(如 Lambda 表达式、方法引用)。
- 引入强大的扩展模型(
@ExtendWith),取代 JUnit4 的@Rule。 - 支持嵌套测试类(
@Nested)、动态测试、重复测试等功能。
- 提供全新的注解(如
-
依赖包 :
xml<dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>5.9.3</version> <scope>test</scope> </dependency>
✅ 这是我们在编写测试代码时主要使用的模块。
(3)JUnit Vintage ------ 对旧版本的兼容支持
-
定位 :为了兼容 JUnit3 和 JUnit4 测试用例而设计的适配层。
-
功能 :
- 让旧项目中已有的 JUnit4 测试(使用
@Test,@Before,@Ignore等注解)能够在 JUnit5 的平台上运行。 - 通过实现一个
TestEngine,将 JUnit4 的测试请求转发给 JUnit4 的核心引擎执行。
- 让旧项目中已有的 JUnit4 测试(使用
-
依赖包 :
xml<dependency> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> <scope>test</scope> </dependency>
⚠️ 注意:从 Spring Boot 2.4 开始,默认不再包含
junit-vintage-engine。这意味着如果你的项目中有 JUnit4 的测试类,必须手动添加该依赖,否则这些测试将不会被执行!
3. JUnit5 与 JUnit4 的关键区别
| 特性 | JUnit4 | JUnit5 |
|---|---|---|
| 注解包路径 | org.junit.* |
org.junit.jupiter.api.* |
| 测试方法修饰符 | 方法必须为 public |
方法可以是 package-private 或更低 |
| 生命周期注解 | @Before, @After, @BeforeClass, @AfterClass |
@BeforeEach, @AfterEach, @BeforeAll, @AfterAll |
| 忽略测试 | @Ignore |
@Disabled |
| 断言类 | org.junit.Assert |
org.junit.jupiter.api.Assertions(支持消息延迟加载) |
| 假设机制 | org.junit.Assume |
org.junit.jupiter.api.Assumptions(更灵活) |
| 扩展模型 | @Rule, @ClassRule |
@ExtendWith + 扩展接口(更强大) |
| 参数化测试 | 需要 @RunWith(Parameterized.class) |
原生支持 @ParameterizedTest |
| Java 版本要求 | Java 5+ | Java 8+(充分利用 Lambda 和 Stream) |
4. Spring Boot 中的 JUnit5 支持
从 Spring Boot 2.2 开始,官方正式将 JUnit5 作为默认的测试框架。Spring Framework 5 也提供了对 JUnit5 的原生支持,例如:
@SpringJUnitConfig和@SpringBootTest可直接与 JUnit5 配合使用。- Spring 的测试上下文框架(TestContext Framework)已适配 JUnit Platform。
- 支持在
@Test方法中使用@Transactional并自动回滚。
📌 提示:在 Spring Boot 项目中,引入
spring-boot-starter-test即可自动包含 JUnit Jupiter 和 Vintage 引擎(但 Vintage 在 2.4+ 中需手动启用)。
5. 架构图解(逻辑示意)
+--------------------------------------------------+
| 开发者编写的测试类 |
| @Test, @BeforeEach, @ParameterizedTest, ... |
+------------------------+-------------------------+
|
+-------------v--------------+ +---------------------+
| JUnit Jupiter API | | JUnit Vintage |
| (org.junit.jupiter.api.*) |<-------->| (兼容 JUnit4 测试) |
+-------------+--------------+-+ +----------+----------+
| | |
+---------v--------+ +----v-------------+ |
| JUnit Platform | | |
| (TestEngine API) | | |
+---------+----------+ | |
| | |
+-----------v----------------------------v-----------v---------+
| IDE / Maven / Gradle |
| (通过 Launcher 启动测试) |
+---------------------------------------------------------------+
6. 总结
JUnit5 不仅仅是一个"新版本"的测试框架,而是一次架构级的革新 。通过将 Platform、Jupiter、Vintage 分离,它实现了:
- ✅ 更好的模块化:各组件职责清晰,易于维护和扩展。
- ✅ 更强的兼容性:平滑迁移旧项目,支持混合测试环境。
- ✅ 更高的灵活性:通过扩展模型支持自定义行为(如日志注入、性能监控等)。
- ✅ 现代化的编程体验:充分利用 Java 8+ 特性,提升测试代码的可读性和表达力。
因此,掌握 JUnit5 不仅是技术升级的需要,更是提升软件质量工程能力的关键一步。接下来我们将深入学习其核心注解的使用方法,帮助你构建高效、可靠的测试体系。
二、JUnit5 常用注解详解(附完整代码案例)
以下我们将逐一介绍 JUnit5 中最常用的注解,并结合实际代码进行演示。
✅ 重要提示:请确保你的项目中引入了正确的依赖。对于 Spring Boot 项目,只需添加:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
该依赖会自动包含 JUnit5 的核心组件。
1. @Test ------ 标记测试方法
这是最基本的注解,用于标识一个方法为测试方法。
java
import org.junit.jupiter.api.Test;
public class JUnit5Test {
@Test
void testDisplayName() {
System.out.println("执行测试方法");
}
}
💡 注意 :JUnit5 使用的是
org.junit.jupiter.api.Test,而非 JUnit4 的org.junit.Test。
2. @DisplayName ------ 设置测试名称(可读性增强)
可以为测试类或测试方法设置自定义显示名称,便于阅读和调试。
java
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@DisplayName("JUnit5 功能测试类")
public class JUnit5Test {
@DisplayName("测试 displayname 注解")
@Test
void testDisplayName() {
System.out.println(1);
}
}
运行时,控制台会显示"测试 displayname 注解"而不是默认的方法名。
3. @BeforeEach ------ 每个测试前执行
在每个测试方法执行之前运行一次,常用于初始化资源。
java
import org.junit.jupiter.api.*;
public class JUnit5Test {
@BeforeEach
void setUp() {
System.out.println("测试就要开始了...");
}
@Test
void testDisplayName() {
System.out.println("执行测试");
}
}
输出:
测试就要开始了...
执行测试
4. @AfterEach ------ 每个测试后执行
在每个测试方法执行之后运行一次,通常用于清理资源。
java
@AfterEach
void tearDown() {
System.out.println("测试结束了...");
}
输出:
测试就要开始了...
执行测试
测试结束了...
5. @BeforeAll ------ 所有测试前执行(静态方法)
在整个测试类的所有测试方法执行前只运行一次,适用于全局初始化。
java
@BeforeAll
static void beforeAll() {
System.out.println("所有测试开始前执行");
}
⚠️ 必须是
static方法!
6. @AfterAll ------ 所有测试后执行(静态方法)
在整个测试类的所有测试方法执行后只运行一次,用于最终清理。
java
@AfterAll
static void afterAll() {
System.out.println("所有测试结束后执行");
}
7. @ParameterizedTest + @ValueSource / @CsvFileSource ------ 参数化测试
允许同一个测试方法使用多个输入参数运行多次。
示例 1:使用 @ValueSource
java
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
@ParameterizedTest
@ValueSource(ints = {1, 2, 3})
void testWithIntegers(int number) {
System.out.println("测试数字: " + number);
}
输出:
测试数字: 1
测试数字: 2
测试数字: 3
示例 2:使用 @CsvFileSource
java
import org.junit.jupiter.params.provider.CsvFileSource;
@ParameterizedTest
@CsvFileSource(files = "src/test/resources/data.csv", delimiter = ',')
void testWithCSV(String name, int age) {
System.out.println(name + " is " + age + " years old");
}
data.csv 内容:
张三,25
李四,30
8. @RepeatedTest ------ 重复执行测试
让某个测试方法重复执行指定次数。
java
import org.junit.jupiter.api.RepeatedTest;
@RepeatedTest(3)
void repeatedTest() {
System.out.println("重复测试第 " + RepeatedTest.currentRepetition() + " 次");
}
输出:
重复测试第 1 次
重复测试第 2 次
重复测试第 3 次
9. @Disabled ------ 跳过测试
临时禁用某个测试方法或类,类似于 JUnit4 的 @Ignore。
java
@Disabled("暂不执行此测试")
@Test
void disabledTest() {
System.out.println("这个测试不会运行");
}
10. @Timeout ------ 设置超时时间
防止测试方法无限运行,超过指定时间则报错。
java
import org.junit.jupiter.api.Timeout;
@Timeout(value = 1000, unit = java.util.concurrent.TimeUnit.MILLISECONDS)
@Test
void timeoutTest() throws InterruptedException {
Thread.sleep(2000); // 超过1秒,会抛出 TimeoutException
}
11. @ExtendWith ------ 扩展测试功能
用于引入自定义扩展,例如 Spring 的 @SpringBootTest 就是基于此实现的。
java
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
@ExtendWith(SpringExtension.class)
class Boot05WebAdminApplicationTests {
@Test
void contextLoads() {
// 测试上下文是否加载成功
}
}
在 Spring Boot 中,
@SpringBootTest已经内置了必要的扩展,无需手动写@ExtendWith。
三、Spring Boot 中的 JUnit5 实践
在 Spring Boot 项目中,我们经常使用如下结构:
java
@SpringBootTest
class Boot05WebAdminApplicationTests {
@Autowired
private UserService userService; // 自动注入
@Test
@Transactional // 测试完成后自动回滚
void testUserSave() {
User user = new User("test", "123");
userService.save(user);
// 断言逻辑...
}
}
✅
@Transactional是 Spring 提供的功能,可在测试中自动开启事务并回滚,避免污染数据库。
四、总结
| 注解 | 作用 |
|---|---|
@Test |
标识测试方法 |
@DisplayName |
设置测试名称 |
@BeforeEach |
每个测试前执行 |
@AfterEach |
每个测试后执行 |
@BeforeAll |
所有测试前执行(静态) |
@AfterAll |
所有测试后执行(静态) |
@ParameterizedTest |
参数化测试 |
@RepeatedTest |
重复执行测试 |
@Disabled |
跳过测试 |
@Timeout |
设置超时 |
@ExtendWith |
扩展测试功能 |
五、写在最后
JUnit5 不仅功能强大,而且设计优雅,极大提升了单元测试的可读性和灵活性。掌握这些常用注解,能够让你的测试代码更加清晰、高效、可靠。
建议在日常开发中养成编写单元测试的习惯,尤其是对核心业务逻辑进行充分覆盖。配合 IntelliJ IDEA 或 Eclipse 等 IDE 的图形化测试运行器,可以快速定位问题,提升开发效率。
📌 推荐实践:为每一个关键方法都编写至少一个单元测试,做到"先写测试,再写代码"(TDD),才能真正构建高质量的软件系统。
📌 参考资料:
希望这篇博客能帮助你全面理解 JUnit5 并快速上手!欢迎点赞收藏,也欢迎在评论区交流你的测试经验 😊