办公文档
用 JUnit 5 参数化测试消除重复用例:同一逻辑跑多组输…
在 Java 中编写数据驱动单元测试,用一份测试逻辑覆盖多组输入、边界值和异常场景。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
用 JUnit 5 参数化测试消除重复用通用
解决思路
- 添加依赖:确保测试类路径上有 junit-jupiter-params(已包含在 junit-jupiter 中),Maven 引入 org.junit.jupiter:junit-jupiter(scope 为 test),Gradle 使用 testImplementation("org.junit.jupiter:junit-jupiter"),断言可另加 assertj-core。
- 选择数据源:简单值用 @ValueSource,表格数据用 @CsvSource,复杂对象用 @MethodSource,枚举用 @EnumSource,自定义提供者用 @ArgumentsSource。
- 匹配参数:测试方法的参数类型和数量必须与数据源提供的数据一一对应。
- 设置显示名:用 name = "{0}..." 让每次调用的输出可读,占位符 {0}、{1} 对应参数位置。
- 验证执行:运行 ./gradlew test --info 或 mvn test,确认所有参数组合都被执行。
- @ValueSource 用法:用 strings、ints 等属性传入简单值,例如 @ValueSource(strings = {"hello", "world", "test"}) 或 @ValueSource(ints = {1, 2, 3, 4, 5})。
- 边界值测试:在 @ValueSource 中显式包含 Integer.MIN_VALUE、-1、0、1、Integer.MAX_VALUE 等极值。
- @CsvSource 用法:用逗号分隔的字符串数组表示每行数据,例如 "alice@example.com, true",方法参数按列顺序接收。
- @MethodSource 用法:指定一个返回 Stream<Arguments> 的静态工厂方法名,用 Arguments.of(...) 构造每组参数,例如 Arguments.of(1, 2, 3)。
- @EnumSource 用法:@EnumSource(Status.class) 遍历全部枚举值;用 names = {"ACTIVE", "INACTIVE"} 只测指定枚举值。
- 自定义显示名:在 @ParameterizedTest(name = "Discount of {0}% should be calculated correctly") 中嵌入参数值。
- 自定义 ArgumentsProvider:实现 ArgumentsProvider 接口的 provideArguments(ExtensionContext context) 方法返回 Stream<? extends Arguments>,再用 @ArgumentsSource(RangeValidatorProvider.class) 引用。
- 异常场景测试:用 @ValueSource(strings = {"", " ", null}) 配合 assertThatThrownBy(() -> Parser.parse(input)).isInstanceOf(IllegalArgumentException.class) 验证非法输入。
- 保持测试逻辑聚焦:每组参数只做单一断言,测试数据按场景分组组织。
- 优先用 @MethodSource 处理复杂对象,用 @CsvSource 处理表格型数据。
- 注意 CSV 转义:含逗号的字符串在 @CsvSource 中必须用单引号包裹。
- 注意 @MethodSource 可见性:工厂方法必须是同一测试类中的 static 方法。
- 注意 @ValueSource 限制:只支持基本类型、字符串和枚举,不能直接传对象或 null。
- 注意执行次数:每组参数都会作为一次独立的测试调用执行。
适用边界 · 注意事项
- 项目未使用 JUnit 5 或无法引入 junit-jupiter-params 依赖时。
- 测试逻辑本身差异较大、无法用同一份断言覆盖多组输入时,参数化反而降低可读性。
- 需要传递复杂对象且无法用 Arguments.of 简洁构造时,直接写独立测试方法更清晰。