当前位置: 首页 > article >正文

3、参数化测试


一、什么是参数化测试?

参数化测试允许使用不同的输入参数多次运行同一个测试方法,从而减少重复代码并提高测试覆盖率。它通过 @ParameterizedTest 注解标记测试方法,并通过指定参数来源(如值列表、CSV、方法等)提供输入数据。

核心特点:
  • 多数据输入:同一测试逻辑用不同参数重复执行。
  • 灵活的参数来源:支持从简单值、CSV、方法、枚举等多种方式获取参数。
  • 可定制显示名称:动态生成测试用例的描述。

二、参数化测试的核心注解和组件

注解/类说明
@ParameterizedTest标记参数化测试方法
@ValueSource提供基本类型(如 int、String)的参数列表
@CsvSource/@CsvFileSource从 CSV 格式的字符串或文件读取参数
@MethodSource从静态方法获取参数
@EnumSource从枚举类型获取参数
@ArgumentsSource自定义参数源(需实现 ArgumentsProvider 接口)
@NullSource/@EmptySource提供 null 或空值参数(如空字符串、集合)
@DisplayName自定义测试名称(支持占位符如 {0}{1}

三、使用场景

  1. 多参数组合测试
    例如测试加法运算的不同输入组合。
  2. 边界值测试
    验证输入值在边界条件下的行为(如最小值、最大值)。
  3. 数据驱动测试
    从外部文件(如 CSV、JSON)加载测试数据。
  4. 枚举值遍历测试
    验证枚举类型的所有可能值。

四、参数化测试的使用步骤

1. 添加依赖

确保 junit-jupiter-params 已添加到项目中:

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-params</artifactId>
    <version>5.9.3</version>
    <scope>test</scope>
</dependency>
2. 编写参数化测试方法

使用 @ParameterizedTest 和参数来源注解。


五、参数来源示例

示例 1:@ValueSource(基本类型)
@ParameterizedTest
@ValueSource(ints = {1, 3, 5, 7})
void testOddNumbers(int number) {
    Assertions.assertTrue(number % 2 != 0);
}

@ParameterizedTest
@ValueSource(strings = {"racecar", "radar", "madam"})
void testPalindromes(String word) {
    Assertions.assertTrue(isPalindrome(word));
}
示例 2:@CsvSource(多参数)
@ParameterizedTest
@CsvSource({
    "2, 3, 5",
    "5, 5, 10",
    "10, -5, 5"
})
void testAddition(int a, int b, int expected) {
    Assertions.assertEquals(expected, a + b);
}
示例 3:@CsvFileSource(从 CSV 文件读取)

创建文件 test-data.csv

a,b,expected
2,3,5
5,5,10
10,-5,5

测试代码:

@ParameterizedTest
@CsvFileSource(resources = "/test-data.csv", numLinesToSkip = 1)
void testAdditionFromCsv(int a, int b, int expected) {
    Assertions.assertEquals(expected, a + b);
}
示例 4:@MethodSource(从方法获取参数)
@ParameterizedTest
@MethodSource("provideStringsForTest")
void testStringLength(String input, int expectedLength) {
    Assertions.assertEquals(expectedLength, input.length());
}

// 提供参数的静态方法(方法名默认与测试方法名匹配)
private static Stream<Arguments> provideStringsForTest() {
    return Stream.of(
        Arguments.of("hello", 5),
        Arguments.of("world", 5),
        Arguments.of("", 0)
    );
}
示例 5:@EnumSource(枚举参数)
enum Day { MONDAY, TUESDAY, WEDNESDAY }

@ParameterizedTest
@EnumSource(Day.class)
void testEnumValues(Day day) {
    Assertions.assertNotNull(day);
}
示例 6:@NullSource@EmptySource
@ParameterizedTest
@NullSource
@EmptySource
@ValueSource(strings = {" ", "   "})
void testNullOrEmptyStrings(String input) {
    Assertions.assertTrue(input == null || input.trim().isEmpty());
}

六、自定义参数显示名称

通过 name 属性自定义测试名称(支持占位符):

@ParameterizedTest(name = "{0} + {1} = {2}")
@CsvSource({
    "2, 3, 5",
    "5, 5, 10"
})
void testAdditionWithCustomName(int a, int b, int expected) {
    Assertions.assertEquals(expected, a + b);
}

输出结果:

√ 2 + 3 = 5
√ 5 + 5 = 10

七、参数类型转换

JUnit 5 支持自动类型转换(如字符串转日期):

@ParameterizedTest
@CsvSource({"2023-01-01, 2023-12-31"})
void testDateRange(LocalDate start, LocalDate end) {
    Assertions.assertTrue(start.isBefore(end));
}
自定义转换器

实现 ArgumentConverter 接口处理复杂类型:

@ParameterizedTest
@CsvSource({"apple, 5", "banana, 6"})
void testCustomConverter(@ConvertWith(FruitConverter.class) Fruit fruit, int length) {
    Assertions.assertEquals(length, fruit.getName().length());
}

static class FruitConverter extends SimpleArgumentConverter {
    @Override
    protected Object convert(Object source, Class<?> targetType) {
        return new Fruit(((String) source).toUpperCase());
    }
}

static class Fruit {
    private final String name;
    Fruit(String name) { this.name = name; }
    public String getName() { return name; }
}

八、参数化测试的生命周期

  • 支持生命周期方法:与普通 @Test 方法一样,参数化测试会触发 @BeforeEach@AfterEach
  • 每个参数独立执行:每个参数组合会作为独立的测试用例运行。

九、参数化测试 vs 动态测试

特性参数化测试动态测试
参数来源预定义参数源(如 CSV、方法)动态生成(运行时逻辑)
用例数量固定(编译时确定)动态(运行时确定)
生命周期支持 @BeforeEach/@AfterEach不支持
适用场景固定参数组合复杂逻辑生成用例

十、总结

参数化测试在以下场景中非常有用:

  • 需要验证多组输入数据的行为。
  • 避免编写重复的测试代码。
  • 数据驱动测试(如从文件或数据库加载数据)。

通过灵活的参数来源和类型转换,可以覆盖广泛的测试需求。对于固定参数组合,优先选择参数化测试;对于运行时动态生成的用例,选择动态测试。


http://www.kler.cn/a/532893.html

相关文章:

  • WordPress自定义.js文件排序实现方法
  • 『 C 』 `##` 在 C 语言宏定义中的作用解析
  • DeepSeek 阐述 2025年前端发展趋势
  • 谈谈你所了解的AR技术吧!
  • 可视化大屏在石油方面的应用。
  • modbus协议处理
  • 【Redis实战】Chapter01-投票后端
  • 『 C++ 』中理解回调类型在 C++ 中的使用方式。
  • Android学习20 -- 手搓App2(Gradle)
  • leetcode 1482. 制作 m 束花所需的最少天数
  • git error: invalid path
  • Redis - String相关命令
  • UE编辑器工具
  • 【自学笔记】Git的重点知识点-持续更新
  • LeetCode:392.判断子序列
  • 接口游标分页
  • 本系统旨在为用户提供一个灵活且可扩展的信息安全管理解决方案,通过插件化的开发模式,使得信息安全的维护更加高效、便捷。
  • 云原生详解:构建未来应用的架构革命
  • 996引擎-怪物:Lua 刷怪+清怪+自动拾取
  • 2025_2_4 C语言中关于free函数及悬空指针,链表的一级指针和二级指指针
  • 【Block总结】CoT,上下文Transformer注意力|即插即用
  • IIC重难点-2
  • 【JavaScript】《JavaScript高级程序设计 (第4版) 》笔记-Chapter2-HTML 中的 JavaScript
  • mysql 学习7 DCL语句,用来管理数据库用户,控制数据库的访问权限
  • k8s二进制集群之各节点部署
  • 【华为OD-E卷 - 跳格子2 100分(python、java、c++、js、c)】