参数化测试:一组数据,一套代码
有时候你要测同一个逻辑在不同输入下的表现,比如:
java
assertTrue(isPrime(2));
assertTrue(isPrime(3));
assertTrue(isPrime(5));
assertTrue(isPrime(7));
assertTrue(isPrime(11));不用参数化测试,就要写五个几乎一模一样的测试方法。JUnit 5 的 @ParameterizedTest 让这件事优雅得多。
@ValueSource:简单数组
最适合参数是简单值的场景:
java
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
class ValueSourceDemo {
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5})
void testIsPositive(int number) {
assertTrue(number > 0);
}
@ParameterizedTest
@ValueSource(strings = {"hello", "world", "JUnit 5"})
void testNotBlank(String input) {
assertFalse(input.isBlank());
}
}@CsvSource:多参数
当需要多个参数时,用 CSV 格式:
java
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
class CsvSourceDemo {
@ParameterizedTest
@CsvSource({
"1, 2, 3",
"4, 5, 9",
"10, 20, 30",
"100, 200, 300"
})
void testAdd(int a, int b, int expected) {
assertEquals(expected, a + b);
}
@ParameterizedTest
@CsvSource({
"'Tom', 25, 'tom@example.com'",
"'Jerry', 30, 'jerry@example.com'",
"'Alice', 28, 'alice@example.com'"
})
void testUser(String name, int age, String email) {
User user = new User(name, age, email);
assertNotNull(user.getName());
assertEquals(age, user.getAge());
}
}@MethodSource:复杂参数
参数来源是方法,可以返回任意类型的集合:
java
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
class MethodSourceDemo {
static List<Object[]> stringProvider() {
return Arrays.asList(
new Object[]{"hello", "HELLO"},
new Object[]{"world", "WORLD"},
new Object[]{"JUnit 5", "JUNIT 5"}
);
}
@ParameterizedTest
@MethodSource("stringProvider")
void testToUpperCase(String input, String expected) {
assertEquals(expected, input.toUpperCase());
}
// 返回 Stream
static Stream<Arguments> stringToLength() {
return Stream.of(
arguments("hello", 5),
arguments("world", 5),
arguments("JUnit", 5)
);
}
@ParameterizedTest
@MethodSource("stringToLength")
void testLength(String input, int expectedLength) {
assertEquals(expectedLength, input.length());
}
}@NullSource 和 @EmptySource
处理 null 和空值:
java
@ParameterizedTest
@ValueSource(strings = {"hello", "world"})
@NullSource // 自动注入 null
@EmptySource // 自动注入空字符串
void testString(String input) {
if (input == null) {
// 测试 null 情况
} else if (input.isEmpty()) {
// 测试空字符串情况
} else {
assertNotNull(input);
}
}常用参数源汇总
| 注解 | 用途 | 示例 |
|---|---|---|
| @ValueSource | 单参数简单值 | @ValueSource(ints = {1, 2, 3}) |
| @CsvSource | 多参数 CSV | @CsvSource({"a,1", "b,2"}) |
| @MethodSource | 方法返回复杂参数 | @MethodSource("providerMethod") |
| @NullSource | null 值 | 与其他源配合使用 |
| @EnumSource | 枚举值 | @EnumSource(Month.class) |
| @ArgumentsSource | 自定义 Arguments Provider | 自定义复杂参数源 |
总结
参数化测试的核心价值:一套代码,多组数据。
适合参数化测试的场景:
- 同一逻辑,多种输入
- 边界值测试(如多个边界值)
- 数据驱动测试(从外部文件读取数据)
当你发现自己在写重复的测试方法时,第一反应应该是:能不能用参数化测试?
