JUnit5常用注解解析(一)

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

1、简介

    JUnit5有别与之前的版本,进行了模块化,它由几个不同的模块组成,这些模块分别来自三个不同的子项目:

        JUnit5 = JUnit Platform + JUnit Jupiter + JUnitVintage

    Junit Platform是在JVM上启动测试框架的基础平台,还定义了TestEngine API,该API可用于开发在平台上运行的测试框架。此外,平台还提供了一个命令行或者GradleMaven插件来启动的控制台启动器。JUnit5的主要目标之一就是让JUnit与其编程客户端(构建工具和IDE)之间的接口更强大和稳定。目的是将发现和执行测试的内部构件和外部必需的所有过滤和配置分离开来。引入了Launcher的概念,可以被用量发现、过滤和执行测试。此外诸如SpockCucumberFitNesse等第三方测试库都可以通过提供自定义的TestEngine来集成到Junit5平台的启动基础设施中。

    Junit Jupiter 是由在Junit 5中编写测试和扩展的新编码模型和扩展模型组成。Jupiter子项目还提供了一个TestEngine,用于在平台上运行基于Jupiter的测试。

     Junit Vintage提供了一个TestEngine,用于在平台上基于JUnit3JUnit4的测试。

2、注解

2.1 标准测试注解

    首先来看一段代码,来通过代码和执行结果来看看JUnit5的这些注解是如何工作的:

import org.junit.jupiter.api.*;

@DisplayName("who am i")
public class BasicTest {

    public BasicTest() {
        System.out.println("Instance");
    }

    @Test
    void firstTest() {
        System.out.println("First test");
    }

    @Tag("second")
    @Test
    void secTest() {
        System.out.println("Second test");
    }

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

    @BeforeAll
    static void beforeStaticRun() {
        System.out.println("BeforeAll");
    }

    @AfterEach
    void afterRun() {
        System.out.println("AfterEach");
    }

    @AfterAll
    static void afterStaticRun() {
        System.out.println("AfterAll");
    }

    @Test
    @Disabled("skip run")
    @DisplayName("厉害了")
    void skipRun() {
        System.out.println("disabled");
    }
}

执行结果:

BeforeAll
Instance
BeforeEach
First test
AfterEach
Instance
BeforeEach
Second test
AfterEach
Instance
skip run
AfterAll

    首先来看@BeforeAll,这个注解的定义是使用了该注解的方法在当前整个测试类中所有的测试方法之前执行,每个测试类运行时只会执行一次。并且这个注解需在static方法上使用,有一种情况例外(声明了TestInstance.Lifecycle.PER_CLASS情况下允许使用在非static方法上)。从上面BasicTest测试类执行结果来看,@BeforeAll注解的方法(执行结果输出BeforeAll)是在这个测试类所有其他测试方法之前执行的,甚至在BasicTest实例化(实例化时输出Instance)之前。

    @AfterAll,与@BeforeAll相对应,这个注解定义是在使用了该注解的方法在当前测试类中所有测试方法都执行完毕后执行的,每个测试类运行时只会执行一次。同样这个注解需要在static方法上使用,有一种例外情况。可以看到上面的输出结果AfterAll是最后一个输出的。

    @BeforeEach,这个注解表示了在每一个测试方法(测试方法表示为所有了@Test、@RepeatedTest、@ParameterizedTest或者@TestFactory注解的方法)之前执行。根据如上接口可以看到每次执行@Test方法之前都会先执行@BeforeEach注解的方法一次(此方法执行输出结果为BeforeEach)。注意不要与@BeforeAll概念混淆,@BeforeEach会每一个测试方法执行之前都会执行,@BeforeAll只会执行一次。

    @AfterEach,此注解表示在每一个测试方法执行之后都会执行。

    @Test,表示该方法是一个测试方法。与JUnit 4的@Test注解不同的是,它没有声明任何属性,因为JUnit Jupiter中的测试扩展是基于它们自己的专用注解来完成的。

    @DisplayName,该注解为测试类或测试方法声明了一个自定义显示的名称,这个是什么意思呢,可以看如下执行结果表,


    使用了@DisplayName测试类和测试方法的名称都被它的定义给覆盖了。

    @Tag,该注解表示为将测试类或者测试方法进行自定名称的标记,使用了标记后可以通过这些标记用来过滤、测试、发现和执行。

    @Disabled,该注解用来禁止整个测试类或者测试方法的执行。如上结果使用了此注解的测试方法skipRun并没有被执行,只输出了skip run的禁止执行声明。

2.2 测试实例的生命周期

    在JUnit5中,为了隔离每个测试方法,每一个测试方法执行的时候默认都会创建一个新的测试类例,如上一章节的测试输出结果。如果希望在同一个实例中执行所有测试方法可以通过在测试类上注解@TestInstance(TestInstance.Lifecycle.PER_CLASS)实现,使用了该模式之后,每一个测试类只会创建一次实例,并且你就可以在非静态方法和接口的方法上声明@BeforeAll和 @AfterAll

    通过JUnit Platform配置文件将默认测试实例生命周期模式设置为Lifecycle.PER_CLASS,你需要在类路径的根目录(例如,src/test/resources)中创建一个名为junit-platform.properties的文件,并写入以下内容。

    junit.jupiter.testinstance.lifecycle.default = per_class

    通过如下代码和执行结果验证@TestInstance(TestInstance.Lifecycle.PER_CLASS)的效果:

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@DisplayName("who am i")
public class BasicTest {

    public BasicTest() {
        System.out.println("Instance");
    }

    @Test
    void firstTest() {
        System.out.println("First test");
    }

    @Tag("second")
    @Test
    void secTest() {
        System.out.println("Second test");
    }

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

    @BeforeAll
    static void beforeStaticRun() {
        System.out.println("BeforeAll");
    }

    @AfterEach
    void afterRun() {
        System.out.println("AfterEach");
    }

    @AfterAll
    void afterStaticRun() {
        System.out.println("AfterAll");
    }

    @Test
    @Disabled("skip run")
    @DisplayName("厉害了")
    void skipRun() {
        System.out.println("disabled");
    }
}

    执行结果:

Instance
BeforeAll
BeforeEach
First test
AfterEach
BeforeEach
Second test
AfterEach
skip run
AfterAll

    可以看到如上代码和上一章代码基本相同只是在BasicTest测试类中加上了@TestInstance(TestInstance.Lifecycle.PER_CLASS)注解,与@AfterAll注解方法改成非静态方法(如上所说,PER_CLASS声明下可以在非静态方法注解@BeforeAll和 @AfterAll)。通过执行结果,Instance只输出了一次,可以得知BasicTest测试类只实例化了一次。

2.3 嵌套测试类

    @Nested,使用了这个注解的测试类表示这是一个嵌套的测试类,什么是嵌套测试类呢通俗的说就是在测试类中嵌套一个非静态的测试类(即内部类),并且可以嵌套多层。通过如下代码和执行结果来理解。

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;

public class NestedTest {

    private List<String> list;

    @Test
    void statement() {
        System.out.println("statement nested test");
    }

    @Nested
    class ListNew{

        @BeforeEach
        void instance() {
            System.out.println("new list");
            list = new ArrayList<>();
        }

        @Test
        void emptyTest() {
            System.out.println("list is empty:" + list.isEmpty());
        }

        @Nested
        class ListData{

            @BeforeEach
            void addData() {
                System.out.println("add data to list");
                list.add("data");
            }

            @Test
            void getData(){
                System.out.println("get data:" + list.get(0));
            }
        }
    }
}

执行结果:

statement nested test
new list
list is empty:true
new list
add data to list
get data:data

    从上面代码可以看到NestedTest测试类中嵌套ListNew测试类,并且ListNew又嵌套了ListData测试类。在执行NestedTest测试类时会同时执行ListNew和ListData中的测试方法。通过执行结果可以发现new list输出结果输出了两次,这个结果是ListNew嵌套类中的@BeforeEach注解了的instance()方法执行的结果,通过输出结果可以明显的得出一个结论,当嵌套类中的测试方法执行时,这个嵌套类上层注解的@BeforeEach是有效的,每次执行嵌套类内的测试方法上层@BeforeEach注解的测试方法会在这个测试方法之前执行。

2.4 条件测试

    什么是条件测试类注解,就是通过这类注解的标识,只有满足条件的测试方法和测试类才会进行执行否这不会进行执行。通过如下代码和执行结果来说明几类条件注解。

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.*;

public class OsConditionTest {

    @Test
    @EnabledOnOs(OS.MAC)
    void onMac() {
        System.out.println("run on mac");
    }

    @Test
    @DisabledOnOs(OS.WINDOWS)
    void disableWindows() {
        System.out.println("disable run on windows");
    }

    @Test
    @DisabledOnOs(OS.LINUX)
    void disableLinux() {
        System.out.println("disable run on linux");
    }

    @Test
    @EnabledOnOs({OS.WINDOWS, OS.LINUX})
    void onWindowsOrLinux() {
        System.out.println("run on windows or linux");
    }

    @Test
    @EnabledOnJre(JRE.JAVA_8)
    void onJre8() {
        System.out.println("run on JRE8");
    }

    @Test
    @EnabledIfEnvironmentVariable(named = "ENV", matches = "*server*")
    void environmentEnable() {
        System.out.println("environment variable enabled");
    }

    @Test
    @EnabledIfSystemProperty(named = "os.arch", matches = ".*64.*")
    void systemProperty() {
        System.out.println("system property enabled");
    }
}

执行结果

Disabled on operating system: Windows 10
run on JRE8
disable run on linux
Environment variable [ENV] does not exist
Disabled on operating system: Windows 10
run on windows or linux
system property enabled

    @EnabledOnOs,表示允许在指定的操作系统上运行指定的测试类或者测试方法。

    @DisabledOnOs,表示禁止在指定的操作系统上运行指定的测试类或者测试方法。

     @EnabledOnJre ,表示运行在指定版本java的运行环境上运行测试类或者方法。

     @DisabledOnJre,表示禁止在指定版本java的运行环境上运行测试类或者方法。

     @EnabledIfSystemProperty ,表示允许在满足JVM系统属性值的情况下运行测试类或者方法。其中name表示属性值的key,matches表示属性值是否匹配(可以用正则表达式)

    @DisabledIfSystemProperty表示禁止在满足JVM系统属性值的情况下运行测试类或者方法。其中name表示属性值的key,matches表示属性值是否匹配(可以用正则表达式)

    @EnabledIfEnvironmentVariable ,表示运允许在满足底层操作系统属性值的情况下运行测试类或者方法。其中name表示属性值的key,matches表示属性值是否匹配(可以用正则表达式)

    @DisabledIfEnvironmentVariable表示运禁止在满足底层操作系统属性值的情况下运行测试类或者方法。其中name表示属性值的key,matches表示属性值是否匹配(可以用正则表达式)


猜你喜欢

转载自blog.csdn.net/guiliguiwang/article/details/80068247