JUnit 5 入门指南 - 02.参数化测试

参数化测试

JUnit通过@ParameterizedTest来实现参数化测试。

引入依赖

JUnit需要Java8以上的运行环境。

Maven
<dependencies>
	<dependency>
		<groupId>org.junit.jupiter</groupId>
		<artifactId>junit-jupiter</artifactId>
		<version>5.6.1</version>
		<scope>test</scope>
	</dependency>
</dependencies>
Gradle
dependencies {
	testImplementation('org.junit.jupiter:junit-jupiter:5.6.1')
}
SHOW ME THE CODE
@ParameterizedTest
@ValueSource(strings = {"a", "b", "c"})
public void test(String s) {
    assertNotNull(s);
}

从代码上来看,@ParameterizedTest申明方式与@Test大体上一致。不过使用@ParameterizedTest时,需要额外申明参数源。

参数源

@ValueSource

@ValueSource是最简单来源之一。它允许你指定一个基本类型的数组,并且它只能为每次调用提供一个参数。

@ParameterizedTest
@ValueSource(strings = {"a", "b", "c"})
public void testWithValueSource(String s) {
    assertNotNull(s);
}
@EnumSource

@EnumSource能够很方便地提供Enum常量。该注解提供了一个可选的names参数,你可以用它来指定使用哪些常量。如果省略了,就意味着所有的常量将被使用,就像下面的例子所示。

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

@EnumSource注解还提供了一个可选的mode参数,它能够细粒度地控制哪些常量将会被传递到测试方法中。

排除模式

@ParameterizedTest
@EnumSource(value = TimeUnit.class, mode = EnumSource.Mode.EXCLUDE, names = {"DAYS", "HOURS"})
public void testWithEnumSourceExclude(TimeUnit timeUnit) {
    assertFalse(EnumSet.of(TimeUnit.DAYS, TimeUnit.HOURS).contains(timeUnit));
}

正則匹配模式

@ParameterizedTest
@EnumSource(value = TimeUnit.class, mode = EnumSource.Mode.MATCH_ALL, names = "^(M|N).+SECONDS$")
public void testWithEnumSourceRegex(TimeUnit timeUnit) {
    String name = timeUnit.name();
    assertTrue(name.startsWith("M") || name.startsWith("N"));
    assertTrue(name.endsWith("SECONDS"));
}
@MethodSource

@MethodSource允许你引用测试类中的一个或多个工厂方法。这些工厂方法必须返回一个Stream、Iterable、Iterator或者参数数组。另外,它们不能接收任何参数。默认情况下,它们必须是static方法,除非测试类使用了@TestInstance(Lifecycle.PER_CLASS)注解。

@ParameterizedTest
@MethodSource("stringProvider")
void testWithSimpleMethodSource(String argument) {
    assertNotNull(argument);
}

static List<String> stringProvider() {
    return Arrays.asList("foo", "bar");
}

如果测试方法声明了多个参数,则需要返回一个Arguments实例的集合,如下面代码所示。

@ParameterizedTest
@MethodSource("stringIntAndListProvider")
void testWithMultiArgMethodSource(String str, int num,List<String> list) {
    assertEquals(3, str.length());
    assertTrue(num >=1 && num <=2);
    assertEquals(2, list.size());
}
static Stream<Arguments> stringIntAndListProvider() {
    return Stream.of(
            Arguments.of("foo", 1, Arrays.asList("a", "b")),
            Arguments.of("bar", 2, Arrays.asList("x", "y"))
    );
}
@CsvSource

@CsvSource允许你将参数列表定义为以逗号分隔的值(即String类型的值)。一个空的引用值’'表示一个空的String;而一个完全空的值被当成一个null引用。如果null引用的目标类型是基本类型,则会抛出一个ArgumentConversionException。

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

@CsvFileSource允许你使用类路径中的CSV文件。CSV文件中的每一行都会触发参数化测试的一次调用。

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

two-column.csv

foo, 1
bar, 2
"baz, qux", 3
@ArgumentsSource

@ArgumentsSource 可以用来指定一个自定义且能够复用的ArgumentsProvider。

@ParameterizedTest
@ArgumentsSource(MyArgumentsProvider.class)
void testWithArgumentsSource(String argument) {
    assertNotNull(argument);
}

static class MyArgumentsProvider implements ArgumentsProvider {
    @Override
    public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
        return Stream.of("foo", "bar").map(Arguments::of);
    }
}

参数类型转化

隐藏转化

为了支持像@CsvSource这样的使用场景,JUnit Jupiter提供了一些内置的隐式类型转换器。转换过程取决于每个方法参数的声明类型。
例如,如果一个@ParameterizedTest方法声明了TimeUnit类型的参数,而实际上提供了一个String,此时字符串会被自动转换成对应的TimeUnit枚举常量

@ParameterizedTest
@ValueSource(strings = "SECONDS")
void testWithImplicitArgumentConversion(TimeUnit argument) {
assertNotNull(argument.name());
}

具体转化列表可查看官方文档。
https://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests-argument-conversion

显示转换

除了使用隐式转换参数,你还可以使用@ConvertWith注解来显式指定一个ArgumentConverter用于某个参数。用户可以参照源码中的@JavaTimeConversionPattern实现自定义转换注解。

@ParameterizedTest
@EnumSource(TimeUnit.class)
void testWithExplicitArgumentConversion(@ConvertWith(ToStringArgumentConverter.class) String argument) {
    assertNotNull(TimeUnit.valueOf(argument));
}

static class ToStringArgumentConverter extends SimpleArgumentConverter {
    @Override
    protected Object convert(Object source, Class<?> targetType) {
        assertEquals(String.class, targetType, "Can only convert to String");
        return String.valueOf(source);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值