08.JUnit参数化测试进阶

52 阅读3分钟

1.自定义数据源

介绍

前文使用了很多种数据源,如果您对它们的各种限制不满意,想要做更彻底的个性化定制,可以开发ArgumentsProvider接口的实现类,并使用@ArgumentsSource指定

代码示例

public class MyArgumentsProvider implements ArgumentsProvider {

    @Override
    public Stream<? extends Arguments> provideArguments(ExtensionContext context) throws Exception {
        return Stream.of("apple4", "banana4").map(Arguments::of);
    }
}
    @Order(15)
    @DisplayName("ArgumentsProvider接口的实现类提供的数据作为入参")
    @ParameterizedTest
    @ArgumentsSource(MyArgumentsProvider.class)
    void argumentsSourceTest(String candidate) {
        log.info("argumentsSourceTest [{}]", candidate);
    }

运行结果

2.参数转换

介绍

参数化测试的数据源和测试方法入参的数据类型必须要保持一致吗?其实JUnit5并没有严格要求,而事实上JUnit5是可以做一些自动或手动的类型转换的

默认示例

如下代码,数据源是int型数组,但测试方法的入参却是double

    @Order(16)
    @DisplayName("int型自动转为double型入参")
    @ParameterizedTest
    @ValueSource(ints = { 1,2,3 })
    void argumentConversionTest(double candidate) {
        log.info("argumentConversionTest [{}]", candidate);
    }

执行结果如下,可见int型被转为double型传给测试方法:

@JavaTimeConversionPattern

介绍

还可以指定转换器,以转换器的逻辑进行转换,下面这个例子就是将字符串转为LocalDate类型

代码示例

    @Order(17)
    @DisplayName("string型,指定转换器,转为LocalDate型入参")
    @ParameterizedTest
    @ValueSource(strings = { "01.01.2017", "31.12.2017" })
    void argumentConversionWithConverterTest(
            @JavaTimeConversionPattern("dd.MM.yyyy") LocalDate candidate) {
        log.info("argumentConversionWithConverterTest [{}]", candidate);
    }

执行结果

3.字段聚合

介绍

方法参数太多,用CSV哪种方式不合适,最好用对象接收,此时可以考虑JUnit5提供的字段聚合功能,也就是将CSV每条记录的所有字段都放入一个ArgumentsAccessor类型的对象中,测试方法只要声明ArgumentsAccessor类型作为入参,就能在方法内部取得CSV记录的所有字段

代码示例

    @Order(18)
    @DisplayName("CsvSource的多个字段聚合到ArgumentsAccessor实例")
    @ParameterizedTest
    @CsvSource({
            "Jane1, Doe1, BIG",
            "John1, Doe1, SMALL"
    })
    void argumentsAccessorTest(ArgumentsAccessor arguments) {
        Person person = new Person();
        person.setFirstName(arguments.getString(0));
        person.setLastName(arguments.getString(1));
        person.setType(arguments.get(2, Types.class));

        log.info("argumentsAccessorTest [{}]", person);
    }

运行结果

更优雅的使用方式

通过注解的方式,指定一个从ArgumentsAccessor到Person的转换器,示例如下,可见测试方法的入参有个注解@AggregateWith,其值PersonAggregator.class就是从ArgumentsAccessor到Person的转换器,而入参已经从前面的ArgumentsAccessor变成了Person

    @Order(19)
    @DisplayName("CsvSource的多个字段,通过指定聚合类转为Person实例")
    @ParameterizedTest
    @CsvSource({
            "Jane2, Doe2, SMALL",
            "John2, Doe2, UNKNOWN"
    })
    void customAggregatorTest(@AggregateWith(PersonAggregator.class) Person person) {
        log.info("customAggregatorTest [{}]", person);
    }

4.测试执行名称自定义

介绍

如下图红框所示,每次执行测试方法,IDEA都会展示这次执行的序号和参数值:

其实上述红框中的内容格式也可以定制,格式模板就是@ParameterizedTest的name属性

代码示例

    @Order(21)
    @DisplayName("CSV格式多条记录入参(自定义展示名称)")
    @ParameterizedTest(name = "序号 [{index}],fruit参数 [{0}],rank参数 [{1}]")
    @CsvSource({
            "apple3, 31",
            "banana3, 32",
            "'lemon3, lime3', 0x3A"
    })
    void csvSourceWithCustomDisplayNameTest(String fruit, int rank) {
        log.info("csvSourceWithCustomDisplayNameTest, fruit [{}], rank [{}]", fruit, rank);
    }

运行结果