jck28 - 小柒 - Junit5动态测试与声明周期L3

一,JUnit5 动态测试

1,概念

  • 动态测试是 JUnit5 中引入的一种新的编程模型。
  • 这种新的测试就是动态测试,它是由@TestFactory注解的工厂方法在运行时生成的。

2,动态测试与静态测试的区别

  • 动态测试:就是DynamicTest在==运行的时候动态的生成测试用例,由@TestFactory注解声明的方法。
  • 静态测试:@Test注解的测试用例,因为该用例在编译时已经完全指定好的。
  • 与@Test方法相比,@TestFactory方法本身不是测试用例,而是测试用例的工厂
    • 所以得到结论:动态测试是工厂的产物。
  • 动态测试DynamicTest与标准 @Test 用例完全不同
    • 执行方式不同:动态测试「DynamicTest」不支持生命周期回调。

3,动态测试构成

(1)方法上必须有 @TestFactory 注解。
(2)由显示名称和Executable组成。
(3)必须有方法返回值类型,类型为「流、集合、迭代器」。

  • Stream Collection Iterable Iterator DynamicNode
  • 不返回会报JUnitException异常
import org.junit.jupiter.api.*;

import java.util.Arrays;
import java.util.Collection;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class DynamicDemoTest {
    @Test
    @DisplayName("静态加法测试用例")
    void add(){
        assertEquals(5,2+3);
    }

    @Test
    @DisplayName("静态除法测试用例")
    void devide(){
        assertEquals(5,25/5);
    }

    //将静态用例转化为动态测试用例
    @TestFactory
    @DisplayName("动态测试用例")
    Collection<DynamicNode> dynamicForCollection(){
        DynamicTest dynamicTest = DynamicTest.dynamicTest("动态加法测试用例", () -> {
            System.out.println("动态加法测试用例运行");
            assertEquals(6, 2 + 14);
        });

        DynamicTest dynamicTest1 = DynamicTest.dynamicTest("动态除法测试用例", () -> {
            assertEquals(6, 36/6);
        });

        return Arrays.asList(dynamicTest,dynamicTest1);
    }
}

4,动态测试创建规则

(1)方法不能是 private ,static修饰。
(2)返回类型必须为: Stream、Collection、Iterable、或IteratorDynamicNode

import com.fasterxml.jackson.databind.ser.std.StdKeySerializers;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.DynamicContainer;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import static org.hamcrest.Matchers.*;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.PrimitiveIterator;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.core.Is.is;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class DynamicNodeTest {

    //DynamicNode返回类型,有两个实例类:DynamicTest和DynamicContainer
    @TestFactory
    @DisplayName("单个动态测试")
    DynamicTest dynamicTest(){
        return DynamicTest.dynamicTest("DynamicTest",() ->{
            assertEquals(7,3+4);
        });
    }

    @TestFactory
    @DisplayName("多个动态测试容器")
    DynamicContainer dynamicContainer(){
        return DynamicContainer.dynamicContainer("DynamicContainer",
                Stream.of(
                        DynamicTest.dynamicTest("加法动态测试",() ->{
                            System.out.println("这是一个加法的测试用例!!");
                            assertEquals(8,4+4);
                        }),
                        DynamicTest.dynamicTest("除法动态测试",() ->{
                            System.out.println("这是一个除法的测试用例!!");
                            assertEquals(8,16/2);
                        })
                )
        );
    }

    @TestFactory
    @DisplayName("Stream单个动态测试")
    Stream<DynamicTest> streamTest(){
        return Stream.of(5,6,7).map(
                arg ->DynamicTest.dynamicTest("Stream动态测试",() ->{
                    System.out.println("参数:"+arg);
                    assertThat(arg,is(greaterThan(2)));
                    }
                )
        );
    }

    @TestFactory
    @DisplayName("Stream多个动态测试")
    Stream<DynamicContainer> streamTest2(){
        return Stream.of(5,6,7).map(
                arg -> DynamicContainer.dynamicContainer("container多个动态测试",
                            Stream.of(
                                    DynamicTest.dynamicTest("Stream大于动态测试", () -> {
                                        System.out.println("参数:" + arg);
                                        assertThat(arg, is(greaterThan(2)));
                                    }),
                                    DynamicTest.dynamicTest("Stream大于动态测试", () -> {
                                                System.out.println("参数:" + arg);
                                                assertThat(arg, is(greaterThan(4)));
                                            }
                                    )
                            )
                    )
        );
    }

    @TestFactory
    @DisplayName("Collection动态测试")
    Collection<DynamicTest> testCollection(){
        Collection<DynamicTest> list = new ArrayList<>();
        DynamicTest dynamicTest = DynamicTest.dynamicTest("Collection加法测试", () -> {
            assertEquals(7, 3 + 4);
        });
        DynamicTest dynamicTest1 = DynamicTest.dynamicTest("Collection除法测试", () -> {
            assertEquals(15, 75/5);
        });
        list.add(dynamicTest);
        list.add(dynamicTest1);
        return list;
    }

    //返回类型为迭代器
    @TestFactory
    @DisplayName("Iterator动态测试")
    Iterator<DynamicTest> testIterator(){
        Collection<DynamicTest> iteratorList = new ArrayList<>();
        PrimitiveIterator.OfInt ofInt = IntStream.iterate(3, n -> n + 1).limit(3).iterator();
        while(ofInt.hasNext()){
            Integer next = ofInt.next();
            DynamicTest dynamicTest = DynamicTest.dynamicTest("n+1动态测试", () -> {
                System.out.println("参数n= " + next);
                assertThat(next, is(greaterThan(2)));
            });
            DynamicTest dynamicTest1 = DynamicTest.dynamicTest("n+2动态测试", () -> {
                int a = next;
                a +=1;
                System.out.println("参数n= " + a);
                assertThat(a, is(greaterThan(3)));
            });
            iteratorList.add(dynamicTest);
            iteratorList.add(dynamicTest1);
        }
        return iteratorList.iterator();
    }

    //返回类型为迭代器元素
    @TestFactory
    @DisplayName("Iterable动态测试")
    Iterable<DynamicTest> testIterable(){
        Collection<DynamicTest> dynamicTests = new ArrayList<>();
        PrimitiveIterator.OfInt ofInt = IntStream.iterate(3, n -> n + 1).limit(3).iterator();
        while(ofInt.hasNext()){
            Integer next = ofInt.next();
            DynamicTest dynamicTest = DynamicTest.dynamicTest("n+1动态测试", () -> {
                System.out.println("参数n= " + next);
                assertThat(next, is(greaterThan(2)));
            });
            DynamicTest dynamicTest1 = DynamicTest.dynamicTest("n+2动态测试", () -> {
                int a = next;
                a +=1;
                System.out.println("参数n= " + a);
                assertThat(a, is(greaterThan(3)));
            });
            dynamicTests.add(dynamicTest);
            dynamicTests.add(dynamicTest1);
        }
        return dynamicTests;
    }
}

5,动态测试生命周期

  • 在动态测试中,@BeforeEach@AfterEach 生命周期方法会针对每个 @TestFactory 方法执行
  • 不会针对每个动态测试执行。
  • 静态测试用例的每一个参数用例都会执行一次BeforeEachAfterEach
  • Dynamic Test的方法只会执行一次BeforeEachAfterEach
    • 默认把BeforeEachAfterEach的生命周期提升为了BeforeAllAfterAll

6,动态测试执行顺序

  • 动态测试的方法顺序不依赖于在顶级测试类上声明的@TestMethodOrder。
  • 为了控制动态测试的顺序,我们可以使用自定义排序。
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;

import java.net.NetworkInterface;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

public class DynamicOrderTest {

    @TestFactory
    @DisplayName("多条动态测试用例")
    List<DynamicTest> dynamicTestCollect(){
        DynamicTest dynamicTest1 = DynamicTest.dynamicTest("1stDy", () -> {
            System.out.println("1stDy");
        });
        DynamicTest dynamicTest2 = DynamicTest.dynamicTest("2ndtDy", () -> {
            System.out.println("2ndDy");
        });
        DynamicTest dynamicTest3 = DynamicTest.dynamicTest("3rdtDy", () -> {
            System.out.println("3rdDy");
        });
        List<DynamicTest> lists = Arrays.asList(dynamicTest1,dynamicTest2,dynamicTest3);
        sortDy(lists);
        return lists;
    }

    //自定义动态测试执行顺序方法
    static void sortDy(List<DynamicTest> lists){
        lists.sort((DynamicTest t1, DynamicTest t2) -> t2.getDisplayName().compareTo(t1.getDisplayName())
        );
    }
}

7,Junit5测试生命周期回调

回调接口:

  • BeforeAllCallback (在所有测试方法 及 @BeforeAll 注解修饰的方法执行之前执行)
  • AfterAllCallback ( 在所有测试方法 及@AfterAll 注解修饰的方法 执行之后执行)
  • BeforeEachCallBack ( 在每个测试方法及 @BeforeEach注解修饰的方法之前执行)
  • AfterEachCallback ( 在每个测试方法及@AfterEach 注解修饰的方法之后执行)
  • BeforeTestExecutionCallback ( 在测试方法@Test 之前立即执行)
  • AfterTestExecutionCallback ( 在测试方法@Test 之后立即执行)

执行顺序:

  1. BeforeAllCallback
  2. BeforeAll
  3. BeforeEachCallback
  4. BeforeEach
  5. BeforeTestExecutionCallback
  6. Test
  7. AfterTestExecutionCallback
  8. AfterEach
  9. AfterEachCallback
  10. AfterAll
  11. AfterAllCallback

通过@ExtendWith 注解进行回调激活

  • 新建一个实体类实现回调接口
import org.junit.jupiter.api.extension.*;

public class CallbackExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback, BeforeTestExecutionCallback,AfterTestExecutionCallback {

    @Override
    public void beforeAll(ExtensionContext extensionContext) throws Exception {
        System.out.println("CallbackExtension  —— BeforeAllCallback");

    }
    @Override
    public void afterAll(ExtensionContext extensionContext) throws Exception {
        System.out.println("CallbackExtension  —— AfterAllCallback");
    }

    @Override
    public void beforeEach(ExtensionContext extensionContext) throws Exception {
        System.out.println("CallbackExtension  —— BeforeEachCallback");
    }

    @Override
    public void afterEach(ExtensionContext extensionContext) throws Exception {
        System.out.println("CallbackExtension  —— AfterEachCallback");
    }

    @Override
    public void beforeTestExecution(ExtensionContext extensionContext) throws Exception {
        System.out.println("CallbackExtension  —— BeforeTestExecutionCallback");
    }

    @Override
    public void afterTestExecution(ExtensionContext extensionContext) throws Exception {
        System.out.println("CallbackExtension  —— AfterTestExecutionCallback");
    }

}

  • 在测试类上加上@ExtendWith 注解进行回调激活,查看测试用例执行顺序
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;


@ExtendWith({CallbackExtension.class})
public class OneTest {
    @BeforeAll
    static void beforeAll(){
        System.out.println("OneTest - BeforeAll"); }

    @BeforeEach
    void beforeEach(){
        System.out.println("OneTest - BeforeEach"); }

    @Test
    void test01(){
        System.out.println("OneTest - test01测试用例"); }

    @Test
    void test02(){
        System.out.println("OneTest - test02测试用例"); }

    @AfterEach
    void afterEach(){
        System.out.println("OneTest - AfterEach"); }

    @AfterAll
    static void afterAll(){
        System.out.println("OneTest - AfterAll"); }

}