【SpringBoot】32 核心功能 - 单元测试 - JUnit5 单元测试中的嵌套测试与参数化测试详解

### 文章目录

  • [@[toc]](#文章目录 @[toc] 前言 🧩 一、什么是嵌套测试?为什么需要它? 💡 概念 🔍 特点 ✅ 示例:测试栈(Stack)的行为 📌 代码解析 🎯 二、什么是参数化测试? 💡 概念 ✅ 支持的注解 🚀 示例 1:使用 @ValueSource 输出: 🚀 示例 2:使用 @EnumSource 🚀 示例 3:使用 @CsvFileSource(CSV 文件) 🚀 示例 4:使用 @MethodSource 🔁 三、JUnit 5 迁移指南 ✅ 示例:迁移前后对比 JUnit 4(旧) JUnit 5(新) 🛠 四、Spring Boot 中如何集成 JUnit 5? 示例:带 Spring 上下文的参数化测试 ✅ 总结 📚 推荐实践 📎 参考资料)
  • [前言](#文章目录 @[toc] 前言 🧩 一、什么是嵌套测试?为什么需要它? 💡 概念 🔍 特点 ✅ 示例:测试栈(Stack)的行为 📌 代码解析 🎯 二、什么是参数化测试? 💡 概念 ✅ 支持的注解 🚀 示例 1:使用 @ValueSource 输出: 🚀 示例 2:使用 @EnumSource 🚀 示例 3:使用 @CsvFileSource(CSV 文件) 🚀 示例 4:使用 @MethodSource 🔁 三、JUnit 5 迁移指南 ✅ 示例:迁移前后对比 JUnit 4(旧) JUnit 5(新) 🛠 四、Spring Boot 中如何集成 JUnit 5? 示例:带 Spring 上下文的参数化测试 ✅ 总结 📚 推荐实践 📎 参考资料)
  • [🧩 一、什么是嵌套测试?为什么需要它?](#文章目录 @[toc] 前言 🧩 一、什么是嵌套测试?为什么需要它? 💡 概念 🔍 特点 ✅ 示例:测试栈(Stack)的行为 📌 代码解析 🎯 二、什么是参数化测试? 💡 概念 ✅ 支持的注解 🚀 示例 1:使用 @ValueSource 输出: 🚀 示例 2:使用 @EnumSource 🚀 示例 3:使用 @CsvFileSource(CSV 文件) 🚀 示例 4:使用 @MethodSource 🔁 三、JUnit 5 迁移指南 ✅ 示例:迁移前后对比 JUnit 4(旧) JUnit 5(新) 🛠 四、Spring Boot 中如何集成 JUnit 5? 示例:带 Spring 上下文的参数化测试 ✅ 总结 📚 推荐实践 📎 参考资料)
  • [💡 概念](#文章目录 @[toc] 前言 🧩 一、什么是嵌套测试?为什么需要它? 💡 概念 🔍 特点 ✅ 示例:测试栈(Stack)的行为 📌 代码解析 🎯 二、什么是参数化测试? 💡 概念 ✅ 支持的注解 🚀 示例 1:使用 @ValueSource 输出: 🚀 示例 2:使用 @EnumSource 🚀 示例 3:使用 @CsvFileSource(CSV 文件) 🚀 示例 4:使用 @MethodSource 🔁 三、JUnit 5 迁移指南 ✅ 示例:迁移前后对比 JUnit 4(旧) JUnit 5(新) 🛠 四、Spring Boot 中如何集成 JUnit 5? 示例:带 Spring 上下文的参数化测试 ✅ 总结 📚 推荐实践 📎 参考资料)
  • [🔍 特点](#文章目录 @[toc] 前言 🧩 一、什么是嵌套测试?为什么需要它? 💡 概念 🔍 特点 ✅ 示例:测试栈(Stack)的行为 📌 代码解析 🎯 二、什么是参数化测试? 💡 概念 ✅ 支持的注解 🚀 示例 1:使用 @ValueSource 输出: 🚀 示例 2:使用 @EnumSource 🚀 示例 3:使用 @CsvFileSource(CSV 文件) 🚀 示例 4:使用 @MethodSource 🔁 三、JUnit 5 迁移指南 ✅ 示例:迁移前后对比 JUnit 4(旧) JUnit 5(新) 🛠 四、Spring Boot 中如何集成 JUnit 5? 示例:带 Spring 上下文的参数化测试 ✅ 总结 📚 推荐实践 📎 参考资料)
  • [✅ 示例:测试栈(Stack)的行为](#文章目录 @[toc] 前言 🧩 一、什么是嵌套测试?为什么需要它? 💡 概念 🔍 特点 ✅ 示例:测试栈(Stack)的行为 📌 代码解析 🎯 二、什么是参数化测试? 💡 概念 ✅ 支持的注解 🚀 示例 1:使用 @ValueSource 输出: 🚀 示例 2:使用 @EnumSource 🚀 示例 3:使用 @CsvFileSource(CSV 文件) 🚀 示例 4:使用 @MethodSource 🔁 三、JUnit 5 迁移指南 ✅ 示例:迁移前后对比 JUnit 4(旧) JUnit 5(新) 🛠 四、Spring Boot 中如何集成 JUnit 5? 示例:带 Spring 上下文的参数化测试 ✅ 总结 📚 推荐实践 📎 参考资料)
  • [📌 代码解析](#文章目录 @[toc] 前言 🧩 一、什么是嵌套测试?为什么需要它? 💡 概念 🔍 特点 ✅ 示例:测试栈(Stack)的行为 📌 代码解析 🎯 二、什么是参数化测试? 💡 概念 ✅ 支持的注解 🚀 示例 1:使用 @ValueSource 输出: 🚀 示例 2:使用 @EnumSource 🚀 示例 3:使用 @CsvFileSource(CSV 文件) 🚀 示例 4:使用 @MethodSource 🔁 三、JUnit 5 迁移指南 ✅ 示例:迁移前后对比 JUnit 4(旧) JUnit 5(新) 🛠 四、Spring Boot 中如何集成 JUnit 5? 示例:带 Spring 上下文的参数化测试 ✅ 总结 📚 推荐实践 📎 参考资料)
  • [🎯 二、什么是参数化测试?](#文章目录 @[toc] 前言 🧩 一、什么是嵌套测试?为什么需要它? 💡 概念 🔍 特点 ✅ 示例:测试栈(Stack)的行为 📌 代码解析 🎯 二、什么是参数化测试? 💡 概念 ✅ 支持的注解 🚀 示例 1:使用 @ValueSource 输出: 🚀 示例 2:使用 @EnumSource 🚀 示例 3:使用 @CsvFileSource(CSV 文件) 🚀 示例 4:使用 @MethodSource 🔁 三、JUnit 5 迁移指南 ✅ 示例:迁移前后对比 JUnit 4(旧) JUnit 5(新) 🛠 四、Spring Boot 中如何集成 JUnit 5? 示例:带 Spring 上下文的参数化测试 ✅ 总结 📚 推荐实践 📎 参考资料)
  • [💡 概念](#文章目录 @[toc] 前言 🧩 一、什么是嵌套测试?为什么需要它? 💡 概念 🔍 特点 ✅ 示例:测试栈(Stack)的行为 📌 代码解析 🎯 二、什么是参数化测试? 💡 概念 ✅ 支持的注解 🚀 示例 1:使用 @ValueSource 输出: 🚀 示例 2:使用 @EnumSource 🚀 示例 3:使用 @CsvFileSource(CSV 文件) 🚀 示例 4:使用 @MethodSource 🔁 三、JUnit 5 迁移指南 ✅ 示例:迁移前后对比 JUnit 4(旧) JUnit 5(新) 🛠 四、Spring Boot 中如何集成 JUnit 5? 示例:带 Spring 上下文的参数化测试 ✅ 总结 📚 推荐实践 📎 参考资料)
  • [✅ 支持的注解](#文章目录 @[toc] 前言 🧩 一、什么是嵌套测试?为什么需要它? 💡 概念 🔍 特点 ✅ 示例:测试栈(Stack)的行为 📌 代码解析 🎯 二、什么是参数化测试? 💡 概念 ✅ 支持的注解 🚀 示例 1:使用 @ValueSource 输出: 🚀 示例 2:使用 @EnumSource 🚀 示例 3:使用 @CsvFileSource(CSV 文件) 🚀 示例 4:使用 @MethodSource 🔁 三、JUnit 5 迁移指南 ✅ 示例:迁移前后对比 JUnit 4(旧) JUnit 5(新) 🛠 四、Spring Boot 中如何集成 JUnit 5? 示例:带 Spring 上下文的参数化测试 ✅ 总结 📚 推荐实践 📎 参考资料)
  • [🚀 示例 1:使用 `@ValueSource`](#文章目录 @[toc] 前言 🧩 一、什么是嵌套测试?为什么需要它? 💡 概念 🔍 特点 ✅ 示例:测试栈(Stack)的行为 📌 代码解析 🎯 二、什么是参数化测试? 💡 概念 ✅ 支持的注解 🚀 示例 1:使用 @ValueSource 输出: 🚀 示例 2:使用 @EnumSource 🚀 示例 3:使用 @CsvFileSource(CSV 文件) 🚀 示例 4:使用 @MethodSource 🔁 三、JUnit 5 迁移指南 ✅ 示例:迁移前后对比 JUnit 4(旧) JUnit 5(新) 🛠 四、Spring Boot 中如何集成 JUnit 5? 示例:带 Spring 上下文的参数化测试 ✅ 总结 📚 推荐实践 📎 参考资料)
  • [输出:](#文章目录 @[toc] 前言 🧩 一、什么是嵌套测试?为什么需要它? 💡 概念 🔍 特点 ✅ 示例:测试栈(Stack)的行为 📌 代码解析 🎯 二、什么是参数化测试? 💡 概念 ✅ 支持的注解 🚀 示例 1:使用 @ValueSource 输出: 🚀 示例 2:使用 @EnumSource 🚀 示例 3:使用 @CsvFileSource(CSV 文件) 🚀 示例 4:使用 @MethodSource 🔁 三、JUnit 5 迁移指南 ✅ 示例:迁移前后对比 JUnit 4(旧) JUnit 5(新) 🛠 四、Spring Boot 中如何集成 JUnit 5? 示例:带 Spring 上下文的参数化测试 ✅ 总结 📚 推荐实践 📎 参考资料)
  • [🚀 示例 2:使用 `@EnumSource`](#文章目录 @[toc] 前言 🧩 一、什么是嵌套测试?为什么需要它? 💡 概念 🔍 特点 ✅ 示例:测试栈(Stack)的行为 📌 代码解析 🎯 二、什么是参数化测试? 💡 概念 ✅ 支持的注解 🚀 示例 1:使用 @ValueSource 输出: 🚀 示例 2:使用 @EnumSource 🚀 示例 3:使用 @CsvFileSource(CSV 文件) 🚀 示例 4:使用 @MethodSource 🔁 三、JUnit 5 迁移指南 ✅ 示例:迁移前后对比 JUnit 4(旧) JUnit 5(新) 🛠 四、Spring Boot 中如何集成 JUnit 5? 示例:带 Spring 上下文的参数化测试 ✅ 总结 📚 推荐实践 📎 参考资料)
  • [🚀 示例 3:使用 `@CsvFileSource`(CSV 文件)](#文章目录 @[toc] 前言 🧩 一、什么是嵌套测试?为什么需要它? 💡 概念 🔍 特点 ✅ 示例:测试栈(Stack)的行为 📌 代码解析 🎯 二、什么是参数化测试? 💡 概念 ✅ 支持的注解 🚀 示例 1:使用 @ValueSource 输出: 🚀 示例 2:使用 @EnumSource 🚀 示例 3:使用 @CsvFileSource(CSV 文件) 🚀 示例 4:使用 @MethodSource 🔁 三、JUnit 5 迁移指南 ✅ 示例:迁移前后对比 JUnit 4(旧) JUnit 5(新) 🛠 四、Spring Boot 中如何集成 JUnit 5? 示例:带 Spring 上下文的参数化测试 ✅ 总结 📚 推荐实践 📎 参考资料)
  • [🚀 示例 4:使用 `@MethodSource`](#文章目录 @[toc] 前言 🧩 一、什么是嵌套测试?为什么需要它? 💡 概念 🔍 特点 ✅ 示例:测试栈(Stack)的行为 📌 代码解析 🎯 二、什么是参数化测试? 💡 概念 ✅ 支持的注解 🚀 示例 1:使用 @ValueSource 输出: 🚀 示例 2:使用 @EnumSource 🚀 示例 3:使用 @CsvFileSource(CSV 文件) 🚀 示例 4:使用 @MethodSource 🔁 三、JUnit 5 迁移指南 ✅ 示例:迁移前后对比 JUnit 4(旧) JUnit 5(新) 🛠 四、Spring Boot 中如何集成 JUnit 5? 示例:带 Spring 上下文的参数化测试 ✅ 总结 📚 推荐实践 📎 参考资料)
  • [🔁 三、JUnit 5 迁移指南](#文章目录 @[toc] 前言 🧩 一、什么是嵌套测试?为什么需要它? 💡 概念 🔍 特点 ✅ 示例:测试栈(Stack)的行为 📌 代码解析 🎯 二、什么是参数化测试? 💡 概念 ✅ 支持的注解 🚀 示例 1:使用 @ValueSource 输出: 🚀 示例 2:使用 @EnumSource 🚀 示例 3:使用 @CsvFileSource(CSV 文件) 🚀 示例 4:使用 @MethodSource 🔁 三、JUnit 5 迁移指南 ✅ 示例:迁移前后对比 JUnit 4(旧) JUnit 5(新) 🛠 四、Spring Boot 中如何集成 JUnit 5? 示例:带 Spring 上下文的参数化测试 ✅ 总结 📚 推荐实践 📎 参考资料)
  • [✅ 示例:迁移前后对比](#文章目录 @[toc] 前言 🧩 一、什么是嵌套测试?为什么需要它? 💡 概念 🔍 特点 ✅ 示例:测试栈(Stack)的行为 📌 代码解析 🎯 二、什么是参数化测试? 💡 概念 ✅ 支持的注解 🚀 示例 1:使用 @ValueSource 输出: 🚀 示例 2:使用 @EnumSource 🚀 示例 3:使用 @CsvFileSource(CSV 文件) 🚀 示例 4:使用 @MethodSource 🔁 三、JUnit 5 迁移指南 ✅ 示例:迁移前后对比 JUnit 4(旧) JUnit 5(新) 🛠 四、Spring Boot 中如何集成 JUnit 5? 示例:带 Spring 上下文的参数化测试 ✅ 总结 📚 推荐实践 📎 参考资料)
  • [JUnit 4(旧)](#文章目录 @[toc] 前言 🧩 一、什么是嵌套测试?为什么需要它? 💡 概念 🔍 特点 ✅ 示例:测试栈(Stack)的行为 📌 代码解析 🎯 二、什么是参数化测试? 💡 概念 ✅ 支持的注解 🚀 示例 1:使用 @ValueSource 输出: 🚀 示例 2:使用 @EnumSource 🚀 示例 3:使用 @CsvFileSource(CSV 文件) 🚀 示例 4:使用 @MethodSource 🔁 三、JUnit 5 迁移指南 ✅ 示例:迁移前后对比 JUnit 4(旧) JUnit 5(新) 🛠 四、Spring Boot 中如何集成 JUnit 5? 示例:带 Spring 上下文的参数化测试 ✅ 总结 📚 推荐实践 📎 参考资料)
  • [JUnit 5(新)](#文章目录 @[toc] 前言 🧩 一、什么是嵌套测试?为什么需要它? 💡 概念 🔍 特点 ✅ 示例:测试栈(Stack)的行为 📌 代码解析 🎯 二、什么是参数化测试? 💡 概念 ✅ 支持的注解 🚀 示例 1:使用 @ValueSource 输出: 🚀 示例 2:使用 @EnumSource 🚀 示例 3:使用 @CsvFileSource(CSV 文件) 🚀 示例 4:使用 @MethodSource 🔁 三、JUnit 5 迁移指南 ✅ 示例:迁移前后对比 JUnit 4(旧) JUnit 5(新) 🛠 四、Spring Boot 中如何集成 JUnit 5? 示例:带 Spring 上下文的参数化测试 ✅ 总结 📚 推荐实践 📎 参考资料)
  • [🛠 四、Spring Boot 中如何集成 JUnit 5?](#文章目录 @[toc] 前言 🧩 一、什么是嵌套测试?为什么需要它? 💡 概念 🔍 特点 ✅ 示例:测试栈(Stack)的行为 📌 代码解析 🎯 二、什么是参数化测试? 💡 概念 ✅ 支持的注解 🚀 示例 1:使用 @ValueSource 输出: 🚀 示例 2:使用 @EnumSource 🚀 示例 3:使用 @CsvFileSource(CSV 文件) 🚀 示例 4:使用 @MethodSource 🔁 三、JUnit 5 迁移指南 ✅ 示例:迁移前后对比 JUnit 4(旧) JUnit 5(新) 🛠 四、Spring Boot 中如何集成 JUnit 5? 示例:带 Spring 上下文的参数化测试 ✅ 总结 📚 推荐实践 📎 参考资料)
  • [示例:带 Spring 上下文的参数化测试](#文章目录 @[toc] 前言 🧩 一、什么是嵌套测试?为什么需要它? 💡 概念 🔍 特点 ✅ 示例:测试栈(Stack)的行为 📌 代码解析 🎯 二、什么是参数化测试? 💡 概念 ✅ 支持的注解 🚀 示例 1:使用 @ValueSource 输出: 🚀 示例 2:使用 @EnumSource 🚀 示例 3:使用 @CsvFileSource(CSV 文件) 🚀 示例 4:使用 @MethodSource 🔁 三、JUnit 5 迁移指南 ✅ 示例:迁移前后对比 JUnit 4(旧) JUnit 5(新) 🛠 四、Spring Boot 中如何集成 JUnit 5? 示例:带 Spring 上下文的参数化测试 ✅ 总结 📚 推荐实践 📎 参考资料)
  • [✅ 总结](#文章目录 @[toc] 前言 🧩 一、什么是嵌套测试?为什么需要它? 💡 概念 🔍 特点 ✅ 示例:测试栈(Stack)的行为 📌 代码解析 🎯 二、什么是参数化测试? 💡 概念 ✅ 支持的注解 🚀 示例 1:使用 @ValueSource 输出: 🚀 示例 2:使用 @EnumSource 🚀 示例 3:使用 @CsvFileSource(CSV 文件) 🚀 示例 4:使用 @MethodSource 🔁 三、JUnit 5 迁移指南 ✅ 示例:迁移前后对比 JUnit 4(旧) JUnit 5(新) 🛠 四、Spring Boot 中如何集成 JUnit 5? 示例:带 Spring 上下文的参数化测试 ✅ 总结 📚 推荐实践 📎 参考资料)
  • [📚 推荐实践](#文章目录 @[toc] 前言 🧩 一、什么是嵌套测试?为什么需要它? 💡 概念 🔍 特点 ✅ 示例:测试栈(Stack)的行为 📌 代码解析 🎯 二、什么是参数化测试? 💡 概念 ✅ 支持的注解 🚀 示例 1:使用 @ValueSource 输出: 🚀 示例 2:使用 @EnumSource 🚀 示例 3:使用 @CsvFileSource(CSV 文件) 🚀 示例 4:使用 @MethodSource 🔁 三、JUnit 5 迁移指南 ✅ 示例:迁移前后对比 JUnit 4(旧) JUnit 5(新) 🛠 四、Spring Boot 中如何集成 JUnit 5? 示例:带 Spring 上下文的参数化测试 ✅ 总结 📚 推荐实践 📎 参考资料)
  • [📎 参考资料](#文章目录 @[toc] 前言 🧩 一、什么是嵌套测试?为什么需要它? 💡 概念 🔍 特点 ✅ 示例:测试栈(Stack)的行为 📌 代码解析 🎯 二、什么是参数化测试? 💡 概念 ✅ 支持的注解 🚀 示例 1:使用 @ValueSource 输出: 🚀 示例 2:使用 @EnumSource 🚀 示例 3:使用 @CsvFileSource(CSV 文件) 🚀 示例 4:使用 @MethodSource 🔁 三、JUnit 5 迁移指南 ✅ 示例:迁移前后对比 JUnit 4(旧) JUnit 5(新) 🛠 四、Spring Boot 中如何集成 JUnit 5? 示例:带 Spring 上下文的参数化测试 ✅ 总结 📚 推荐实践 📎 参考资料)

前言

在现代 Java 开发中,JUnit 5 已成为单元测试的事实标准。它不仅带来了更简洁的 API 和更强的扩展性,还引入了诸如 嵌套测试(Nested Tests)参数化测试(Parameterized Tests) 等强大特性,极大提升了测试代码的可读性和维护性。

本文将结合你提供的截图内容,深入讲解 JUnit 5 在 Spring Boot 项目中的两个核心功能:

  • ✅ 嵌套测试(Nested Testing)
  • ✅ 参数化测试(Parameterized Testing)

并附上完整的示例代码和详细解析。


🧩 一、什么是嵌套测试?为什么需要它?

💡 概念

嵌套测试 是 JUnit 5 提供的一种组织测试用例的方式,允许你在类中定义内部类,并使用 @Nested 注解来表示这些内部类是"测试上下文的一部分"。通过这种方式,你可以把相关的测试逻辑分组,提高代码结构清晰度。

类似于测试套件中的"子模块"概念。

🔍 特点

  • 支持任意深度嵌套。
  • 内部类可以有自己的 @BeforeEach, @AfterEach 方法。
  • 可以用于模拟不同场景下的行为(如:正常情况 vs 异常情况)。
  • 更好地表达测试意图。

✅ 示例:测试栈(Stack)的行为

java 复制代码
import org.junit.jupiter.api.*;
import java.util.Stack;

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TestingAStackDemo {

    private Stack<Object> stack;

    @Test
    @DisplayName("is instantiated with new Stack()")
    void isInstantiatedWithNew() {
        new Stack<>();
    }

    @Nested
    @DisplayName("when new")
    class WhenNew {

        @BeforeEach
        void createNewStack() {
            stack = new Stack<>();
        }

        @Test
        @DisplayName("is empty")
        void isEmpty() {
            Assertions.assertTrue(stack.isEmpty());
        }

        @Test
        @DisplayName("has size zero")
        void hasSizeZero() {
            Assertions.assertEquals(0, stack.size());
        }
    }

    @Nested
    @DisplayName("after pushing an element")
    class AfterPushing {

        String anElement = "an element";

        @BeforeEach
        void pushAnElement() {
            stack.push(anElement);
        }

        @Test
        @DisplayName("is no longer empty")
        void isNotEmpty() {
            Assertions.assertFalse(stack.isEmpty());
        }

        @Test
        @DisplayName("returns the element when popped")
        void returnsElementWhenPopped() {
            Assertions.assertEquals(anElement, stack.pop());
        }

        @Test
        @DisplayName("returns the element when peeked")
        void returnsElementWhenPeeked() {
            Assertions.assertEquals(anElement, stack.peek());
        }
    }
}

📌 代码解析

关键点 解释
@Nested 标记该内部类为一个"测试上下文",其内的测试方法会继承外部类的配置,但也可以有自己独立的生命周期钩子。
@BeforeEach 在每个测试方法执行前调用,适合初始化资源。
@DisplayName 给测试方法或类起一个描述性的名字,方便阅读输出结果。
@TestInstance(TestInstance.Lifecycle.PER_CLASS) 表示整个测试类实例在整个测试过程中只创建一次,避免重复初始化。

✅ 这样组织后,测试逻辑清晰地分为"新建时"、"压入元素后"两种状态,便于理解和维护。


🎯 二、什么是参数化测试?

💡 概念

参数化测试 允许你使用不同的输入值运行同一个测试方法多次,而无需为每种情况写一个单独的测试方法。

这非常适合验证边界条件、异常处理、多种数据类型等场景。

✅ 支持的注解

注解 功能说明
@ValueSource 从数组提供基本类型或字符串参数
@EnumSource 使用枚举值作为参数
@CsvFileSource 从 CSV 文件读取参数
@MethodSource 从某个静态方法返回的流获取参数
@NullSource 提供 null 值
@EmptySource 提供空集合/数组

🚀 示例 1:使用 @ValueSource

java 复制代码
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

public class ParameterizedTestExample {

    @ParameterizedTest
    @ValueSource(strings = {"one", "two", "three"})
    @DisplayName("参数化测试")
    void parameterizedTest(String string) {
        System.out.println("测试输入: " + string);
        Assertions.assertNotNull(string);
    }
}
输出:
复制代码
测试输入: one
测试输入: two
测试输入: three

✔️ 三个不同的字符串自动被传入测试方法,共执行三次。


🚀 示例 2:使用 @EnumSource

java 复制代码
enum Status {
    PENDING, ACTIVE, INACTIVE, COMPLETED
}

@ParameterizedTest
@EnumSource(Status.class)
void testStatusValues(Status status) {
    System.out.println("当前状态:" + status);
    Assertions.assertNotNull(status);
}

✔️ 自动遍历所有枚举值进行测试。


🚀 示例 3:使用 @CsvFileSource(CSV 文件)

假设有一个文件 data.csv 内容如下:

csv 复制代码
1,apple
2,banana
3,orange
java 复制代码
@ParameterizedTest
@CsvFileSource(resources = "/data.csv", delimiter = ',')
void testFromCSV(int id, String fruit) {
    System.out.println("ID: " + id + ", Fruit: " + fruit);
    Assertions.assertTrue(id > 0);
    Assertions.assertNotNull(fruit);
}

✅ 从资源路径加载 CSV 文件,逐行解析为参数。


🚀 示例 4:使用 @MethodSource

java 复制代码
import java.util.stream.Stream;

@ParameterizedTest
@MethodSource("provideStrings")
void testWithMethodSource(String input) {
    Assertions.assertNotNull(input);
    Assertions.assertTrue(input.length() > 0);
}

static Stream<String> provideStrings() {
    return Stream.of("hello", "world", "test", "");
}

✅ 静态方法返回 Stream<T>,JUnit 会自动将其展开成多个测试用例。


🔁 三、JUnit 5 迁移指南

当你从 JUnit 4 升级到 JUnit 5 时,需要注意以下变更:

JUnit 4 JUnit 5
org.junit.Assert org.junit.jupiter.api.Assertions
@Before / @After @BeforeEach / @AfterEach
@BeforeClass / @AfterClass @BeforeAll / @AfterAll
@Ignore @Disabled
@Category @Tag
@RunWith, @Rule, @ClassRule @ExtendWith

✅ 示例:迁移前后对比

JUnit 4(旧)
java 复制代码
@Before
public void setUp() {
    stack = new Stack<>();
}

@Ignore("暂不支持")
@Test
public void testSomething() {
    // ...
}
JUnit 5(新)
java 复制代码
@BeforeEach
void setUp() {
    stack = new Stack<>();
}

@Disabled("暂不支持")
@Test
void testSomething() {
    // ...
}

✅ 注意:@Disabled 是推荐方式,语义更明确;@ExtendWith 可以用来注册自定义扩展器,比如 Mockito 的 MockitoExtension


🛠 四、Spring Boot 中如何集成 JUnit 5?

确保你的 pom.xml 包含以下依赖:

xml 复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

spring-boot-starter-test 默认已包含 JUnit 5、Mockito、AssertJ 等。

示例:带 Spring 上下文的参数化测试

java 复制代码
@SpringBootTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class UserServiceTest {

    @Autowired
    private UserService userService;

    @ParameterizedTest
    @ValueSource(strings = {"admin", "user", "guest"})
    void shouldReturnUserByRole(String role) {
        User user = userService.findByRole(role);
        Assertions.assertNotNull(user);
        Assertions.assertEquals(role, user.getRole());
    }
}

✅ 结合 Spring 的自动装配能力,可以在真实上下文中进行参数化测试。


✅ 总结

特性 优势 适用场景
嵌套测试 结构清晰,便于组织相关测试 多状态测试(如:初始化、操作后、异常)
参数化测试 减少重复代码,提升效率 边界值、多输入组合、大量数据验证
JUnit 5 迁移 更现代、更灵活 所有新项目应优先采用

📚 推荐实践

  1. 使用 @DisplayName 让测试名称更具可读性;
  2. 合理使用 @Nested 分组测试逻辑;
  3. 利用 @ParameterizedTest 处理批量数据;
  4. 使用 @CsvFileSource@MethodSource 实现复杂参数源;
  5. 配合 Spring Boot 的 @SpringBootTest@AutoConfigureTestDatabase 实现完整集成测试。

📎 参考资料


相关推荐
小坏讲微服务2 小时前
Nginx集群与SpringCloud Gateway集成Nacos的配置指南
spring boot·nginx·spring cloud·gateway
计算机学姐3 小时前
基于SpringBoot的新闻管理系统【协同过滤推荐算法+可视化统计】
java·vue.js·spring boot·后端·spring·mybatis·推荐算法
一 乐3 小时前
远程在线诊疗|在线诊疗|基于java和小程序的在线诊疗系统小程序设计与实现(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·小程序
serendipity_hky4 小时前
【微服务 - easy视频 | day04】Seata解决分布式事务
java·spring boot·分布式·spring cloud·微服务·架构
大菠萝学姐4 小时前
基于springboot的旅游攻略网站设计与实现
前端·javascript·vue.js·spring boot·后端·spring·旅游
q_19132846955 小时前
基于SpringBoot+Vue2的美食菜谱美食分享平台
java·spring boot·后端·计算机·毕业设计·美食
刘一说6 小时前
Spring Boot 中的定时任务:从基础调度到高可用实践
spring boot·后端·wpf
小坏讲微服务6 小时前
使用 Spring Cloud Gateway 实现集群
java·spring boot·分布式·后端·spring cloud·中间件·gateway
没有bug.的程序员6 小时前
Spring Cloud Gateway 路由与过滤器机制
java·开发语言·spring boot·spring·gateway