写 JUnit 测试的时候,你是不是也遇到过这种情况?同一个方法要测好几种输入,比如验证手机号格式,正常号、空值、短号、字母组合都要测,结果就得写四五个测试方法,代码长得像复制粘贴的,改一处逻辑就得改一串地方?其实啊,用参数化测试就能解决这个问题,今天兔子哥教你 5 分钟搞定,以后再也不用写重复代码了!
一、参数化测试到底是什么?为啥非要用它?
可能有新手朋友会问,参数化测试听着挺复杂,它到底是啥?说白了,就是用不同参数重复跑同一个测试逻辑,不用手动写多个测试方法。比如测加法函数,输入(1+1)、(2+3)、(0+0)这些 case,参数化测试能让你一次性定义好所有参数,自动跑遍所有情况。
那为啥非得用它呢?你想啊,如果不用参数化,测 5 种情况就得写 5 个测试方法,方法名还得区分开,比如 testAdd1、testAdd2,看着就乱。要是后来逻辑改了,每个方法都得改一遍,漏改一个就可能出问题。参数化测试呢,测试逻辑只写一次,参数单独列出来,改逻辑只改一处,参数想加就加,多方便。
兔子哥之前维护过一个校验工具类,各种规则加起来要测 20 多种情况,刚开始用普通测试写了 20 个方法,后来改了校验规则,改得手都酸了。换成参数化测试后,代码量少了一半,维护起来也轻松多了。
二、想用参数化测试?先把依赖和注解搞明白
要在 JUnit 5 里用参数化测试,得先做两件事:加依赖、认注解。别担心,步骤很简单,新手也能搞定。
第一步:加依赖,参数化测试才能跑起来
如果是 Maven 项目,在 pom.xml 里加这段依赖(Spring Boot 项目可能已经包含了,先检查一下):
xml
<dependency><groupId>org.junit.jupitergroupId><artifactId>junit-jupiter-paramsartifactId><scope>testscope>dependency>这是 JUnit 5 专门的参数化测试依赖,不加的话后面的注解会报错,这步可别忘了。
第二步:核心注解记三个,基本够用了
参数化测试有好几个注解,但新手记三个最常用的就行:
- @ParameterizedTest:标记这个方法是参数化测试,替代普通的 @Test 注解
- @ValueSource:最简单的参数来源,支持字符串、数字、布尔值等,比如 @ValueSource (strings = {"13800138000", "13900139000"})
- @MethodSource:从方法获取参数,适合复杂参数(比如对象、多类型参数),后面实战会讲到
有朋友会问:这些注解记不住咋办?没关系,先用起来,写两次就记住了,谁也不是一开始就全背下来的。
三、实战示例:5 分钟写个参数化测试案例
光说不练假把式,咱们拿 “验证手机号格式” 这个场景举例,手把手写一个参数化测试,看完你就会了。
假设咱们有个手机号校验工具类:
java
public class PhoneValidator {// 简单校验:11位数字,以1开头public static boolean isValid(String phone) {if (phone == null) {return false;}return phone.matches("^1\\d{10}$");}}现在要测这几种情况:正常手机号、空值、10 位数字、12 位数字、含字母的,用参数化测试怎么做?
第一步:写测试类,加 @ParameterizedTest 注解
测试方法不用 @Test 了,改用 @ParameterizedTest,告诉 JUnit 这是参数化测试:
java
class PhoneValidatorTest {// 这里写参数化测试方法}第二步:用 @ValueSource 定义参数和预期结果
因为要测 “输入手机号” 和 “预期是否有效”,单靠 @ValueSource 不够,得用 @CsvSource(CSV 格式的参数,逗号分隔输入和预期结果):
java
@ParameterizedTest// CSV格式:第一个是输入手机号,第二个是预期结果(布尔值)@CsvSource({"13800138000, true", // 正常手机号"null, false", // 空值"138001380, false", // 10位数字"138001380000, false", // 12位数字"1380013800a, false" // 含字母})void testIsValid(String phone, boolean expected) {// 处理null参数(CSV里的"null"是字符串,需要转成null)if ("null".equals(phone)) {phone = null;}// 调用要测试的方法boolean result = PhoneValidator.isValid(phone);// 断言结果是否符合预期assertEquals(expected, result);}第三步:运行测试,看结果
右键运行这个测试方法,JUnit 会自动把 5 组参数分别跑一遍,控制台能看到每个参数的执行结果,哪个通了哪个没通一目了然。以前要写 5 个方法,现在一个方法搞定,是不是省事儿多了?
四、复杂参数怎么办?@MethodSource 来帮忙
刚才用的 @CsvSource 适合简单参数,如果参数是对象、数组,或者需要动态生成,就得用 @MethodSource 了。比如测试用户年龄校验,参数是 User 对象和预期结果,咱们试试:
第一步:定义提供参数的方法
这个方法得是静态的,返回 Stream 或集合,里面装着测试参数:
java
// 提供测试参数的方法,必须是静态的static Stream<Arguments> userAgeArgs() {return Stream.of(Arguments.of(new User(18), true), // 18岁合法Arguments.of(new User(17), false), // 17岁不合法Arguments.of(new User(60), true), // 60岁合法Arguments.of(new User(0), false) // 0岁不合法);}第二步:测试方法用 @MethodSource 引用这个方法
java
@ParameterizedTest// 引用上面定义的参数方法名@MethodSource("userAgeArgs")void testUserAgeValid(User user, boolean expected) {boolean result = UserValidator.isAgeValid(user);assertEquals(expected, result);}这样就能用复杂对象做参数了,灵活多了吧?兔子哥做项目时,遇到需要查数据库或接口获取测试数据的情况,都用 @MethodSource 动态生成参数,特别方便。
五、新手常踩的坑,这些地方要注意
参数化测试不难,但新手容易在这些地方出错,兔子哥提醒你避开:
坑 1:忘了加参数化依赖,注解报错
刚学的时候,兔子哥就犯过这错,写了 @ParameterizedTest 却没加 junit-jupiter-params 依赖,结果注解红一片,还以为是 JUnit 版本问题。记住,参数化测试是单独的依赖,必须加上。
坑 2:参数类型不匹配,测试跑不起来
比如 @CsvSource 里传的是数字,测试方法参数写成 String,就会报类型转换错误。解决办法:要么统一类型,要么在测试方法里手动转换(比如把字符串转成数字)。
坑 3:@MethodSource 方法不是静态的
这个最容易忘!@MethodSource 引用的方法必须是 static 的,不然会报错 “MethodSource ... must be static”,新手经常在这卡壳,记牢了啊。
六、兔子哥的小建议,让参数化测试更顺手
用了这么久参数化测试,有几个小技巧分享给你:
第一,参数命名要清晰。在 @ParameterizedTest 后面可以加 name 属性,比如 @ParameterizedTest (name = "手机号 {0} 验证结果应为 {1}"),这样运行时能看到每个参数的描述,结果更直观。
第二,别贪多,参数分组管理。如果一个测试方法参数太多(比如超过 10 组),可以按场景拆分,比如正常场景、异常场景各写一个参数化方法,看着清楚。
第三,结合断言工具用。除了 assertEquals,还可以用 assertTrue、assertFalse,复杂对象可以用 assertThat,让断言更灵活。
其实啊,参数化测试最大的好处就是 “一次编写,多次运行”,特别适合边界值测试、等价类划分这些需要多组参数的场景。刚开始可能觉得注解多、规则多,但练两个例子就会发现,比写一堆重复测试方法轻松多了。
现在项目里,兔子哥只要遇到需要多参数测试的情况,必用参数化测试,代码量少了,维护效率高了,还能避免漏测。新手朋友别害怕,赶紧找个自己的项目试试,5 分钟就能上手,相信我,用过一次你就会爱上它!
标签: junit-jupiter-params org.junit.jupiter
版权声明:除非特别标注,否则均为本站原创文章,转载时请以链接形式注明文章出处。
还木有评论哦,快来抢沙发吧~