### 文章目录
- [@[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 迁移 | 更现代、更灵活 | 所有新项目应优先采用 |
📚 推荐实践
- 使用
@DisplayName让测试名称更具可读性; - 合理使用
@Nested分组测试逻辑; - 利用
@ParameterizedTest处理批量数据; - 使用
@CsvFileSource或@MethodSource实现复杂参数源; - 配合 Spring Boot 的
@SpringBootTest和@AutoConfigureTestDatabase实现完整集成测试。