【SpringBoot】30 核心功能 - 单元测试 - JUnit5 单元测试简介与常用注解实战详解

文章目录

  • [一、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)
    • [8. `@RepeatedTest` ------ 重复执行测试](#8. @RepeatedTest —— 重复执行测试)
    • [9. `@Disabled` ------ 跳过测试](#9. @Disabled —— 跳过测试)
    • [10. `@Timeout` ------ 设置超时时间](#10. @Timeout —— 设置超时时间)
    • [11. `@ExtendWith` ------ 扩展测试功能](#11. @ExtendWith —— 扩展测试功能)
  • [三、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 的核心引擎执行。
  • 依赖包

    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 并快速上手!欢迎点赞收藏,也欢迎在评论区交流你的测试经验 😊

相关推荐
cj6341181508 小时前
网卡驱动架构以及源码分析
java·后端
Sincerelyplz8 小时前
【JDK新特性】分代ZGC到底做了哪些优化?
java·jvm·后端
zs宝8 小时前
Java 限流简易实现
后端
韩立学长8 小时前
【开题答辩实录分享】以《植物病虫害在线答疑小程序的设计与实现》为例进行答辩实录分享
spring boot·小程序·vue
玛卡巴卡019 小时前
Maven 从入门到实战:搞定依赖管理与 Spring Boot 项目构建
java·spring boot·maven
国服第二切图仔9 小时前
Rust开发之使用panic!处理不可恢复错误
开发语言·后端·rust
جيون داد ناالام ميづ9 小时前
Spring Boot 核心原理(一):基础认知篇
java·spring boot·后端
南囝coding10 小时前
现代Unix命令行工具革命:30个必备替代品完整指南
前端·后端
夏之小星星10 小时前
Springboot结合Vue实现分页功能
vue.js·spring boot·后端