JUnit5常用注解解析(二)

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/guiliguiwang/article/details/80300387

1. 能被自动注册解析器解析的参数

    Junit5中扩展定义了ParameterResolver,它可以在运行时动态解析参数。如果一个测试的构造函数或者测试@Test、@BeforeEach、@BeforeAll等方法接收参数,这个参数必须被已注册的ParamterResolver解析。

    下面介绍几个能被自动注册的内置解析器解析的参数,其他参数解析器必须通过@ExtendWith声明来注册开启(这就是扩展的功能)。先看代码和执行结果:

import org.junit.jupiter.api.*;

public class ParameterResolverTest {

    @Test
    @Tag("test-tag")
    void testInfo(TestInfo info) {
        System.out.println(info.getTags());
    }


    @RepeatedTest(4)
    void repeated(RepetitionInfo info, TestReporter testReporter) {
        testReporter.publishEntry("currentRepetition:", info.getCurrentRepetition() + "");
    }

    @Test
    void report(TestReporter reporter) {
        reporter.publishEntry("a key", "a key");
    }

}

执行结果:

exclude patterns:[test-tag]
timestamp = 2018-05-13T16:45:08.210, a key = a key
timestamp = 2018-05-13T16:45:08.249, currentRepetition: = 1
timestamp = 2018-05-13T16:45:08.266, currentRepetition: = 2
timestamp = 2018-05-13T16:45:08.287, currentRepetition: = 3
timestamp = 2018-05-13T16:45:08.304, currentRepetition: = 4

    TestInfo:如果方法参数是TestInfo,TestInfoParamterResolver解析器会自动为测试方法提供一个TestInfo实例用于填充参数的值。TestInfo对象主要包含一些当前测试的形象,如显示名称、测试类、测试方法或tag等。如上testInfo方法运行结果为输出这个方法声明的Tag的值test-tag。

    TestReporter:如果方法参数是TestReporter,自动注册的解析器TestReporterParamterResolver提供实例化的参数,可以被用来发布有关当前测试运行的其他数据。通过查看report方法输出结果可以看出,通过TestReporter的publishEntry方法可以在测试报告中输出你想输出的一些信息并且包含运行时间。

    @RepeatedTest注解:可以用来定义一个测试方法重复执行的次数,每个重复测试的调用就和执行@Test方法一样。

    RepetitionInfo:如果方法参数是RepetitionInfo,RepetitionInfoParamterResolver会自动为@RepeateTest注释过的测试方式提供RepetitionInfo实例(也可以用于@RepeateTest测试方法的@BeforeEach和@AfterEach,不能用于非@RepeateTest测试方法,否则会抛出异常)。RepetitionInfo对象主要记录了被用来检索测试方法当前重复以及总重复次数等相关信息。

2.参数化测试

     @ParameterizedTest:参数化测试可以通过来源于不同的参数源的不同参数多次运行,运行的测试方法和@Test一样(在当前5.2版本中这还只是个试验性功能)。参数化测试有多类参数源来源:

public class ParameterMethodTest{
    
    @ParameterizedTest
    @ValueSource(strings = {"1", "2", "3"})
    void stringSource(String value) {
        System.out.println(value);
    }

    @ParameterizedTest
    @EnumSource(value = CRLReason.class, mode = EnumSource.Mode.MATCH_ALL, names = "^(C|N)A_COMPROMISE")
    void enumSource(CRLReason reason){
        System.out.println(reason);
    }

    @ParameterizedTest
    @MethodSource("getStringSet")
    void methodSource(String value) {
        System.out.println(value);
    }

    static Set<String> getStringSet() {
        Set<String> set = new HashSet<>();
        set.add("method one");
        set.add("method two");
        return set;
    }

    @ParameterizedTest
    @MethodSource("getArgumentSet")
    void methodArgumentSource(String value, String str, List list) {
        System.out.println(value + str + list.toString());
    }

    static Stream getArgumentSet() {
        return Stream.of(
                Arguments.of("one", "1", Arrays.asList("a", "b")),
                Arguments.of("two", "2", Arrays.asList("x", "y"))
        );
    }

    @ParameterizedTest
    @CsvSource({"csv one, 1", "'csv, two', 2"})
    void csvSource(String value, int num, TestReporter reporter) {
        reporter.publishEntry("a", value + num);
    }

    @ParameterizedTest
    @ArgumentsSource(Provider.class)
    void argumentSource(String argument) {
        System.out.println(argument);
    }

    static class Provider implements ArgumentsProvider {

        @Override
        public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
            return Stream.of("argument one", "argument two").map(Arguments::of);
        }
    }
}

运行结果:

argument one
argument two
CA_COMPROMISE
one1[a, b]
two2[x, y]
timestamp = 2018-05-13T20:00:05.324, a = csv one1
timestamp = 2018-05-13T20:00:05.337, a = csv, two2
1
2
3
method one
method two

    @ValueSource:声明一个基本类型的数组(String、int、long、double等),并且为测试方法提供调用参数,每个参数执行一次测试方法。

    @EnumSource:为测试方法提供Enum常量参数,这个注释提供一个可选的name参数,通过其指定使用那些常量,如:names = {"DAY", "HOURS"}。还有一个可选的mode参数,能够通过names中声明的常量列表或者正则表达式来细粒度地控制那些常量将会被传递到测试方法中。

    @MethodSource:可是通过这个注释引用测试类中的一个或者多个工厂方法,这些方法必须返回一个Stream、Interable、Iterator或者数组参数,工厂方法不能接收任何参数。默认情况下必须是static方法,除非声明了@TestInstace(Lifecycle.PER_CLASS)。如果测试方法中有多个参数,则需要返回一个Arguments实例的集合。

    @CsvSource:在参数化测试方法中,通过参数列表声明参数,每组参数的不同值用逗号隔开。

    @CsvFileSource:使用类路径中的CSV文件作为参数,每一行数据都会触发参数化测试的一次调用。

    @ArgumentsSource:指定一个实现ArgumentsProvider接口的参数提供类作为参数化测试方法的参数提供者,这个类的provideArguments方法返回的Stream作为参数流。

3.动态测试

    之前常用的声明测试方法注解@Test可以认为是静态测试方法,是在编译时指定的。在JUnit5中还有一类测试称为动态测试,可以在运行时改变。

     @TestFactory:声明动态测试,声明的方法本身不是测试用例,而是生成测试用例的工厂方法。这个方法必须返回一个DynamicNode实例的Stream、Collection、Iterable或Iterator。DynamicNode有两个可实例化子类DynamicContainer和DynamicTest。动态测试的执行生命周期和@Test测试不同,同一个@TestFactory方法所生成的n个动态测试,@BeforeEach和@AfterEach只会在n个动态测试开始前和结束后执行一次,不会为每个单独的动态测试都执行。例子:

public class Dynamic {
  
    @TestFactory
    Collection<DynamicTest> dynamicTestsFromCollection() {
        return Arrays.asList(
                DynamicTest.dynamicTest("1st dynamic test", () -> assertTrue(true)),
                DynamicTest.dynamicTest("2nd dynamic test", () -> assertEquals(4, 2 * 2))
        );
    }

    @TestFactory
    Stream<DynamicNode> dynamicTestsWithContainers() {
        return Stream.of("A", "B", "C")
                .map(input -> DynamicContainer.dynamicContainer("Container " + input, Stream.of(
                        DynamicTest.dynamicTest("not null", () -> assertNotNull(input)),
                        DynamicContainer.dynamicContainer("properties", Stream.of(
                                DynamicTest.dynamicTest("length > 0", () -> assertTrue(input.length() > 0)),
                                DynamicTest.dynamicTest("not empty", () -> assertFalse(input.isEmpty()))
                        ))
                )));
    }

    @BeforeEach
    void before() {
        System.out.println("before");
    }
}

    dynamicTestsWithContainers()方法执行结果的结构如下,DynamicContainer可以动态的创建任意嵌套的动态测试节点层次结构(DynamicTest)。


猜你喜欢

转载自blog.csdn.net/guiliguiwang/article/details/80300387
今日推荐