JUnit 5 参数化测试完全指南
同一测试逻辑使用不同数据多次执行,实现数据驱动测试
概述
参数化测试允许同一测试逻辑使用不同数据多次执行,实现数据驱动的测试方式。
启用参数化测试
xml
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>@ValueSource
整数数组
java
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5})
@DisplayName("整数是否为正数")
void testPositiveNumbers(int number) {
assertTrue(number > 0);
}字符串数组
java
@ParameterizedTest
@ValueSource(strings = {"hello", "world", "JUnit"})
@DisplayName("字符串长度测试")
void testStringLength(String word) {
assertTrue(word.length() > 0);
}其他类型
java
@ValueSource(longs = {1L, 2L, 3L})
@ValueSource(doubles = {0.1, 0.2, 0.3})
@ValueSource(classes = {String.class, Integer.class})@CsvSource
基本用法
java
@ParameterizedTest
@CsvSource({
"1, 2, 3",
"4, 5, 9",
"10, 20, 30"
})
@DisplayName("加法测试:a + b = expected")
void testAddition(int a, int b, int expected) {
assertEquals(expected, a + b);
}空值和 null
java
@ParameterizedTest
@CsvSource({
"'hello', 5", // 字符串 'hello',长度 5
"'', 0", // 空字符串,长度 0
", 0" // null 作为缺失值(需配置)
})
void testStringLength(String input, int expected) { }CSV 文件来源
java
@ParameterizedTest
@CsvFileSource(resources = "/test-data/calculator.csv", numLinesToSkip = 1)
void testFromCsvFile(int a, int b, int expected) {
assertEquals(expected, a + b);
}@MethodSource
静态方法来源
java
@ParameterizedTest
@MethodSource("arithmeticProvider")
void testAddition(int a, int b, int expected) {
assertEquals(expected, a + b);
}
// 必须返回 Stream<Arguments>
static Stream<Arguments> arithmeticProvider() {
return Stream.of(
Arguments.of(2, 3, 5),
Arguments.of(10, 5, 15),
Arguments.of(-1, 1, 0)
);
}外部类方法来源
java
@ParameterizedTest
@MethodSource("com.example.TestDataFactory#createOrders")
void testOrders(Order order) {
assertNotNull(order);
}@EnumSource
基本用法
java
@ParameterizedTest
@EnumSource(OperatingSystem.class)
void testOperatingSystems(OperatingSystem os) {
assertNotNull(os);
}过滤选项
java
@ParameterizedTest
@EnumSource(value = Month.class, names = {"JANUARY", "FEBRUARY", "MARCH"})
void testQuarterMonths(Month month) {
assertTrue(month.getValue() <= 3);
}
// 排除特定值
@EnumSource(value = Month.class, names = ".+BER", mode = EnumSource.Mode.EXCLUDE)
void testNonDecember(Month month) {
assertNotEquals(Month.DECEMBER, month);
}空值和空来源
@NullSource / @EmptySource
java
@ParameterizedTest
@NullAndEmptySource
@ValueSource(strings = {"a", "b", "c"})
void testStrings(String value) {
// 测试: null, "", "a", "b", "c"
assertTrue(value == null || !value.isEmpty());
}@ArgumentsSource(自定义来源)
java
public class CustomArgumentsProvider implements ArgumentsProvider {
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return Stream.of(
Arguments.of("input1", 1),
Arguments.of("input2", 2)
);
}
}
// 使用
@ParameterizedTest
@ArgumentsSource(CustomArgumentsProvider.class)
void testWithCustomSource(String input, int expected) { }自定义参数转换器
java
public class LocalDateConverter extends SimpleArgumentConverter {
@Override
protected Object convert(Object input, Class<?> targetType) {
if (targetType == LocalDate.class && input instanceof String) {
return LocalDate.parse((String) input);
}
throw new IllegalArgumentException("Cannot convert");
}
}
// 使用
@ParameterizedTest
@CsvSource({"2024-01-01, NEW_YEAR", "2024-12-25, CHRISTMAS"})
void testHoliday(
@ConvertWith(LocalDateConverter.class) LocalDate date,
HolidayType type) { }@DisplayName 模板
java
@ParameterizedTest(name = "计算 {0} + {1} 应等于 {2}")
@CsvSource({"1, 2, 3", "4, 5, 9"})
void testAddition(int a, int b, int expected) {
assertEquals(expected, a + b);
}输出示例:
✔ 计算 1 + 2 应等于 3
✔ 计算 4 + 5 应等于 9最佳实践
使用有意义的显示名称
java@DisplayName("用户名 [{0}] 验证应 {1}")将复杂数据封装为对象
javastatic Stream<OrderTestCase> orderTestCases() { return Stream.of( new OrderTestCase("正常商品", 100.0, 10.0, 110.0), new OrderTestCase("折扣商品", 100.0, 20.0, 80.0) ); } record OrderTestCase(String name, double price, double shipping, double total) { }避免过多参数(建议 ≤ 5 个)
结合条件执行
java@ParameterizedTest @ValueSource(ints = {1, 2, 3}) @EnabledOnOs(OS.LINUX) // 仅 Linux 执行 void linuxOnly(int value) { }
相关页面
- **Java\JUnit5-完全指南** - 返回总览
- **Java\JUnit5-注解体系** - @ParameterizedTest 注解
- **Java\JUnit5-断言机制** - 断言在参数化测试中的应用
- **Java\Mockito-完全指南** - Mock 与参数化结合
#标签 #junit #参数化 #parameterized #testing #data-driven