Skip to content

参数化测试:一组数据,一套代码

有时候你要测同一个逻辑在不同输入下的表现,比如:

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")
@NullSourcenull 值与其他源配合使用
@EnumSource枚举值@EnumSource(Month.class)
@ArgumentsSource自定义 Arguments Provider自定义复杂参数源

总结

参数化测试的核心价值:一套代码,多组数据

适合参数化测试的场景:

  • 同一逻辑,多种输入
  • 边界值测试(如多个边界值)
  • 数据驱动测试(从外部文件读取数据)

当你发现自己在写重复的测试方法时,第一反应应该是:能不能用参数化测试?

基于 VitePress 构建