junit4用了这么久,来试试junit5中的参数化测试提高单测效率吧

大家好,这里是小奏 ,觉得文章不错可以关注公众号小奏技术

junit5之前的单元测试

为了更好的说明junit5的参数化测试,我们先来看看junit5之前不使用参数测试后的的单元测试是怎么写的。

比如我们现在编写了一个邮箱验证的工具类,

java 复制代码
public class StringValidator {
    
    public static boolean isValidEmail(String email) {
        return email != null && email.matches("(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])");
    }
}

不使用参数化测试

我们需要对这个工具类进行单元测试,我们可以这样写:

java 复制代码
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

public class StringUtilsTest {

    @Test
    void testIsValidEmail() {
        assertTrue(StringValidator.isValidEmail("xiaozou@example.com"));
        assertTrue(StringValidator.isValidEmail("xiao.zou@domain.com"));
        assertTrue(StringValidator.isValidEmail("xiaozou+tag@example.co.uk"));

        assertFalse(StringValidator.isValidEmail("xiaozou@email"));
        assertFalse(StringValidator.isValidEmail("@xiaozou.com"));
        assertFalse(StringValidator.isValidEmail("no at sign"));
        assertFalse(StringValidator.isValidEmail(null));
    }
}

运行结果

可以看到这么多测试用例就只有一个运行结果

使用参数化测试

首先引入junut5的依赖:

xml 复制代码
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.8.2</version>
            <scope>test</scope>
        </dependency>

然后我们使用junit5中的参数化测试功能来编写单元测试

我们可以将上面的测试用例改写成如下形式:

java 复制代码
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.junit.jupiter.params.provider.NullSource;
import static org.junit.jupiter.api.Assertions.*;

public class StringValidatorTest {

    @ParameterizedTest
    @ValueSource(strings = {
        "xiaozou@example.com",
        "xiao.zou@domain.com",
        "xiaozou+tag@example.co.uk"
    })
    void testValidEmails(String email) {
        assertTrue(StringValidator.isValidEmail(email));
    }

    @ParameterizedTest
    @ValueSource(strings = {
        "xiaozou@email",
        "@xiaozou.com",
        "no at sign"
    })
    @NullSource
    void testInvalidEmails(String email) {
        assertFalse(StringValidator.isValidEmail(email));
    }

运行结果

可以看到使用参数化测试后,我们仅需要将自己需要测试的参数写入到注解中即可运行多次,而不再需要写多个测试用例

而且测试报告相比之前更加清晰,可以看到每个测试用例的执行结果

参数化测试常用注解说明

ParameterizedTest

@ParameterizedTest注解用于指定一个参数化测试方法,该方法将会被多次调用,每次调用时传入不同的参数。和@Test效果类似,主要是用于标记。

@ValueSource

@ValueSource注解用于指定一个或多个常量值,这些值将会被传入到测试方法中。

@ValueSource支持8中基本的数据类型,以及String类型,Class类型。

如果要使用枚举参数我们可以使用@EnumSource注解

@EnumSource

java 复制代码
@ParameterizedTest
@EnumSource(TimeUnit.class)
void testWithEnumSource(TimeUnit timeUnit) {
    assertNotNull(timeUnit);
}

@ParameterizedTest
@EnumSource(value = TimeUnit.class, names = { "DAYS", "HOURS" })
void testWithEnumSourceInclude(TimeUnit timeUnit) {
    assertTrue(EnumSet.of(TimeUnit.DAYS, TimeUnit.HOURS).contains(timeUnit));
}

@EnumSource中的names属性用于指定枚举类型中的枚举值,如果不指定,则默认使用所有的枚举值。

更多用法可以参考 doczhcn.gitbook.io/junit5/inde...

@CsvSource

@CsvSource主要用于传入多个参数。

比如我们可以传入输入和预期结果进行校验

java 复制代码
@ParameterizedTest
@CsvSource({"xiaoZou,XIAOZOU", "golang,GOLANG", "java,JAVA"})
void toUpperCase_ShouldGenerateTheExpectedUppercaseValue(String input, String expected) {
    String actualValue = input.toUpperCase();
    assertEquals(expected, actualValue);
}

当然也可以就正常传入多个参数测试

java 复制代码
@ParameterizedTest
@CsvSource({ "foo, 1", "bar, 2", "'baz, qux', 3" })
void testWithCsvSource(String first, int second) {
    assertNotNull(first);
    assertNotEquals(0, second);
}

@CsvFileSource

@CsvFileSource让你使用classpath中的CSV文件。CSV文件中的每一行都会导致参数化测试的一次调用。

java 复制代码
@ParameterizedTest
@CsvFileSource(resources = "/two-column.csv")
void testWithCsvFileSource(String first, int second) {
    assertNotNull(first);
    assertNotEquals(0, second);
}

类似读取excel. 如果有表头,可以使用numLinesToSkip = 1来跳过表头

总结

参数化测试是junit5中的一个非常有用的特性,使用参数化测试(@ParameterizedTest)有如下好处

  1. 代码更简洁:每个测试方法只需编写一次断言逻辑,减少了重复代码
  2. 更易于维护:添加新的测试用例只需在 @ValueSource 中添加新的值,而不需要编写新的测试方法
  3. 更好的可读性:测试用例和测试逻辑分离,使得测试意图更加清晰
  4. 更好的测试报告:每个参数都作为单独的测试用例运行,在测试报告中可以清楚地看到每个用例的结果。

junit5提供了非常多的新特性,我们推荐大家使用junit5来编写单元测试,更多junit5的特性可以参考官方文档。

如果我们是在spring-boot中进行编写单元测试,spring-boot-test中默认是引入了junit5依赖的,所以我们可以直接使用junit5的特性。

参考

相关推荐
小码编匠4 分钟前
WPF 绘制图表合集-LiveCharts
后端·c#·.net
codervibe12 分钟前
MySQL 命令行连接与企业级远程访问实践(含故障排查与安全策略)
数据库·后端
workflower16 分钟前
测试套件缩减方法
数据库·单元测试·需求分析·个人开发·极限编程
codervibe17 分钟前
metasploit中用shodan模块进行网络摄像头查找
后端
程序员爱钓鱼21 分钟前
Python编程实战 面向对象与进阶语法 迭代器与生成器
后端·python·ipython
Cikiss26 分钟前
图解 MySQL JOIN
数据库·后端·mysql
程序员爱钓鱼30 分钟前
Python编程实战 面向对象与进阶语法 JSON数据读写
后端·python·ipython
Mintopia36 分钟前
🌐 《GraphQL in Next.js 初体验》中文笔记
前端·后端·全栈
爱吃烤鸡翅的酸菜鱼43 分钟前
深度解析《AI+Java编程入门》:一本为零基础重构的Java学习路径
java·人工智能·后端·ai
林太白1 小时前
rust12-路由接口
后端·rust