本文共 4035 字,大约阅读时间需要 13 分钟。
作为《JUnit5学习》系列的第七篇,本篇将在前文《JUnit5学习》系列第六篇《JUnit5参数化测试实战》基础上,深入探讨JUnit5参数化测试的高级功能,包括自定义数据源、参数转换、字段聚合以及测试执行名称自定义等内容。
在JUnit5的参数化测试中,除了内置的多种数据源(如@CsvSource、@ValueSource等),开发者有时会对数据源的灵活性和定制性有更高的要求。为了实现更深层次的定制,可以通过开发自定义的ArgumentsProvider接口实现类,并使用@ArgumentsSource注解指定数据源。
示例代码:
package com.bolingcavalry.parameterized.service.impl;import org.junit.jupiter.api.extension.ExtensionContext;import org.junit.jupiter.params.provider.Arguments;import org.junit.jupiter.params.provider.ArgumentsProvider;import java.util.stream.Stream;public class MyArgumentsProvider implements ArgumentsProvider { @Override public Stream provideArguments(ExtensionContext context) throws Exception { return Stream.of("apple4", "banana4") .map(Arguments::of); }} 在测试方法中添加@ArgumentsSource(MyArgumentsProvider.class)注解:
@Order(15)@DisplayName("ArgumentsProvider接口的实现类提供的数据作为入参")@ParameterizedTest@ArgumentsSource(MyArgumentsProvider.class)void argumentsSourceTest(String candidate) { log.info("argumentsSourceTest [{}]", candidate);} JUnit5的参数化测试允许数据源与测试方法的入参类型之间进行自动或手动转换。虽然不一定要求两者类型一致,但通过转换器可以实现更灵活的数据类型适配。
示例代码:
@Order(16)@DisplayName("int型自动转为double型入参")@ParameterizedTest@ValueSource(ints = {1, 2, 3})void argumentConversionTest(double candidate) { log.info("argumentConversionTest [{}]", candidate);} 当使用@ValueSource注解时,int型数据会自动转换为double型传递给测试方法。
更复杂的转换:
@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);} 通过@JavaTimeConversionPattern注解,可以将字符串日期格式转换为LocalDate类型。
传统的参数化测试方法需要为每个数据源字段定义一个独立的参数,这在数据源字段较多时会导致代码冗余。JUnit5提供了Argument Aggregation功能,允许将多个字段聚合到一个ArgumentsAccessor对象中。
示例代码:
@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对象,可以使用JUnit5提供的@AggregateWith注解,指定一个从ArgumentsAccessor到目标类型的转换器。
转换器实现:
package com.bolingcavalry.parameterized.service.impl;import org.junit.jupiter.params.aggregator.ArgumentsAccessor;import org.junit.jupiter.params.aggregator.ArgumentsAggregationException;import org.junit.jupiter.params.aggregator.ArgumentsAggregator;public class PersonAggregator implements ArgumentsAggregator { @Override public Object aggregateArguments(ArgumentsAccessor arguments, ParameterContext context) throws ArgumentsAggregationException { Person person = new Person(); person.setFirstName(arguments.getString(0)); person.setLastName(arguments.getString(1)); person.setType(arguments.get(2, Types.class)); return 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);} 通过@ParameterizedTest注解的name属性,可以为每次测试执行定义一个友好的名称格式。例如:
@Order(20)@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);} 通过本文的实践,熟悉了JUnit5参数化测试的高级功能,包括自定义数据源、参数转换、字段聚合以及测试执行名称自定义等。在实际项目中,合理使用这些功能可以显著提升单元测试的代码质量和效率。
转载地址:http://uhtkz.baihongyu.com/