一、Junit5的架构

JUnit 5由三个不同子项目中的几个不同模块组成。JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage,如下:

  • JUnit platform:为了能够启动 junit 测试,IDE、构建工具或者插件需要包含和扩展平台 API。它定义了用于开发在平台上运行的新测试框架的“TestEngine”API。它还提供了一个控制台启动器来从命令行启动平台并为 Gradle 和 Maven 构建插件。
  • JUnit Jupiter:它包括用于编写测试的新编程和扩展模型。它具有所有新的 junit 注释和 TestEngine实现来运行使用这些注释编写的测试。
  • JUnit Vintage:它的主要目的是支持在 JUnit 5 平台上运行 JUnit 3 和 JUnit 4 书面测试。它有向后兼容性。

参考网站如下:

官网地址:https://junit.org/junit5/
官方入门文档:https://junit.org/junit5/docs/current/user-guide/#overview
官方例子:https://github.com/junit-team/junit5-samples
官方github:https://github.com/junit-team

二、安装

可以在 maven 或者 gradle 项目中使用 JUnit 5,下面展示在Maven中使用:

<dependencies>
        <!--配置和加载测试计划的公共API – 典型的使用场景是IDE和构建工具-->
        <dependency>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-launcher</artifactId>
            <version>1.7.0</version>
            <scope>test</scope>
        </dependency>

        <!--JUnit Jupiter测试引擎的实现,仅仅在运行时需要。-->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.7.0</version>
            <scope>test</scope>
        </dependency>

        <!--JUnit Vintage测试引擎实现,允许在新的JUnit Platform上运行低版本的JUnit测试,即那些以JUnit 3或JUnit 4风格编写的测试。
        非必须
        -->
        <dependency>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
            <version>5.7.0</version>
            <scope>test</scope>
        </dependency>
        <!--编写测试 和 扩展 的JUnit Jupiter API。-->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.7.0</version>
        </dependency>

    </dependencies>

三、JUnit 5 注释

JUnit 5 提供以下注释来编写测试:

Annotation

描述

@BeforeEach

带注释的方法将在测试类中的每个测试方法之前运行。

@AfterEach

带注释的方法将在测试类中的每个测试方法之后运行。

@BeforeAll

带注释的方法将在测试类中的所有测试方法之前运行。这个方法必须是静态的。

@AfterAll

带注释的方法将在测试类中的所有测试方法之后运行。这个方法必须是静态的。

@Test

它用于将方法标记为junit测试

@DisplayName

用于为测试类或者测试方法提供任何自定义显示名称

@Disable

它用于禁用或者忽略测试套件中的测试类或者方法。

@Nested

用于创建嵌套测试类

@Tag

用标签标记测试方法或者测试类以进行测试发现和过滤

@TestFactory

标记方法是动态测试的测试工厂

@ParameterizedTest

表示方法是参数化测试

@RepeatedTest

 表示方法是重复测试模板

@ExtendWith

用于注册自定义扩展,该注解可以继承

@FixMethodOrder(MethodSorters.NAME_ASCENDING)

控制测试类中方法执行的顺序,这种测试方式将按方法名称的进行排序,由于是按字符的字典顺序,所以以这种方式指定执行顺序会始终保持一致;不过这种方式需要对测试方法有一定的命名规则,如 测试方法均以testNNN开头(NNN表示测试方法序列号 001-999)

四、案例演示

4.1.测试:Hello World

创建Maven项目,导入Junit5依赖后,创建包 com.easybuy.method,在下面创建 类  Calculator(模拟我们需要测试的功能):

public class Calculator {

    public int add(int num1, int num2){
        return num1+num2;
    }

    public int sub(int num1, int num2){
        return num1-num2;
    }
}

创建com.easybuy.test包,在下面编写测试用例,创建 HelloWorld,代码如下:

public class HelloWorld {
    @Test
    public void testSub(){
        Calculator calculator = new Calculator();
        //调用减法
        int num = calculator.sub(10, 12);
        //分别传入预期结果和实际结果
        Assertions.assertEquals(-12,num);
    }

    @Test
    public void testAdd(){
        Calculator calculator = new Calculator();
        //调用减法
        int num = calculator.add(10, 12);
        //分别传入预期结果和实际结果
        Assertions.assertEquals(22,num);
    }
}

点击 类名或者方法名左边的按钮即可执行用例如下:

Junit5 单元测试_Test

@Test注解在方法上标记方法为测试方法,以便构建工具和 IDE 能够识别并执行它们。

注意的用例在idea中执行的时候一直报错缺少:org.junit.jupiter.api,这个问题很奇怪,需要再idea中勾选如下配置,其他的项目不用勾选

Junit5 单元测试_测试类_02

4.2.给用例添加上前置和后置

添加上前置和后置后,完成的案例代码如下:

public class AppTest {
    @BeforeAll
    static void setup(){
        System.out.println("@BeforeAll 将在测试类中的所有测试方法之前运行。这个方法必须是静态的");
    }

    @BeforeEach
    void setupThis(){
        System.out.println("@BeforeEach 将在测试类中的每个测试方法之前运行");
    }


    @Test
    public void testSub(){
        Calculator calculator = new Calculator();
        //调用减法
        int num = calculator.sub(10, 12);
        //分别传入预期结果和实际结果
        Assertions.assertEquals(-2,num);
        System.out.println("我是testSub");
    }

    @Test
    public void testAdd(){
        Calculator calculator = new Calculator();
        //调用减法
        int num = calculator.add(10, 12);
        //分别传入预期结果和实际结果
        Assertions.assertEquals(22,num);
        System.out.println("我是testAdd");
    }

    @AfterEach
    void tearThis(){
        System.out.println("@AfterEach 将在测试类中的每个测试方法之后运行。");
    }

    @AfterAll
    static void tear(){
        System.out.println("@AfterAll 将在测试类中的所有测试方法之后运行。这个方法必须是静态的");
    }
}

执行后结果如:

Junit5 单元测试_测试类_03

4.3.禁用执行某条用例

@Disabled注解可以添加给某个用例方法,或者类上,添加后面该类或者该方法不会在执行

Junit5 单元测试_测试方法_04

五、断言测试

断言有助于使用测试用例的实际输出来验证预期输出。为了简单起见,所有 JUnit Jupiter 断言都是 org.junit.jupiter.Assertions 类中的静态方法,例如

  • assertEquals 断言预期值和实际值相等
  • assertNotEquals 断言预期值和实际值不相等,则测试通过
  • assertAll 分组断言,执行其中包含的所有断言
  • assertArrayEquals 断言预期数组和实际数组相等
  • assertFalse 断言条件为假
  • assertNotNull 断言不为空
  • assertSame 断言两个对象相等
  • assertTimeout 断言超时
  • fail 使单元测试失败

案例代码如下:

public class AssertionsTest {

    @Test
    public void test1(){
        //Test will pass
        Assertions.assertEquals(3,3);
    }

    @Test
    public void test2(){
        //Test will fail
        Assertions.assertEquals(4, 4);
    }

    @Test
    public void test3(){
        int[] ints1 = new int[1024];
        int[] ints2 = new int[1024];

        //Test will fail 断言预期数组和实际数组相等
        Assertions.assertArrayEquals(ints1,ints2);
    }


    @Test
    public void test4(){
        ArrayList<String> strings = new ArrayList<>();
        strings.add("李明月");
        strings.add("刘明月");
        strings.add("张如月");

        //Test will fail 断言预期集合中是否包含某个值
        Assertions.assertTrue(strings.contains("张如月"));
    }
}

六、参数化测试

6.1.引入参数化所需依赖

实现参数化需要将下面的依赖引入到pom.xml,注意版本要和之前引入的Junit版本保持一致

dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-params</artifactId>
            <version>5.7.0</version>
            <scope>compile</scope>
        </dependency>

6.2.使用注解ParameterizedTest

使用@ParameterizedTest注解来代替@Test注解,单参数化注解使用@ValueSource ,多参数注解 @CsvSource,如果这两个注解@ParameterizedTest@Test同时使用则会多执行一次

  • 单个参数如下:
public class ParamsTest {
    //@ParameterizedTest注解来代替@Test注解,单参数化注解使用@ValueSource
    @ParameterizedTest
    @ValueSource(strings = {"张三", "小明", "小红"})//几个参数,就执行几次
    void testParamString(String name){
        System.out.println(name);
    }


    @ParameterizedTest
    @ValueSource(ints = {10,20,30})
    void testParamInt(int num){
        Assertions.assertTrue(num < 100);
        System.out.println(num);
    }
}
  • 多参数时,默认参数之间用英文逗号隔开
public class ParamsTest {
    //@ParameterizedTest注解来代替@Test注解,单参数化注解使用@ValueSource
    @ParameterizedTest
    @ValueSource(strings = {"张三", "小明", "小红"})//几个参数,就执行几次
    void testParamString(String name){
        System.out.println(name);
    }


    @ParameterizedTest
    @ValueSource(ints = {10,20,30})
    void testParamInt(int num){
        Assertions.assertTrue(num < 100);
        System.out.println(num);
    }

    //多参数
    @ParameterizedTest
    @CsvSource({"小明,10,一班","小李,11,二班"})//多参数使用@CsvSource注解实现,这里传入了两组数据,分别传入中间使用逗号隔开
    public void MultParams(String name,int age,String className ){
        System.out.println("name:"+name+" age:"+age+" classname:"+className);
    }

}
  • 参数文件:参数可以从指定csv文件中读取,在resources目录下创建csv文件,参数之间用相同分隔符分割,例如 “,”,"|"等。此时使用的注解为 @CsvFileSource

下面例如创建一个csv文件如data.csv,需要注意如果csv文件中有中文,需要通过记事本打开csv文件修改编码方式为utf-8,否则读取进来后中文会乱码,数据创建多列直接使用逗号隔开,存放在resource目录下:

Junit5 单元测试_Test_05

在代码中读取csv文件中的数据作为参数化的信息如下:

public class ParamsTest {
    //@ParameterizedTest注解来代替@Test注解,单参数化注解使用@ValueSource
    @ParameterizedTest
    @ValueSource(strings = {"张三", "小明", "小红"})//几个参数,就执行几次
    void testParamString(String name){
        System.out.println(name);
    }


    @ParameterizedTest
    @ValueSource(ints = {10,20,30})
    void testParamInt(int num){
        Assertions.assertTrue(num < 100);
        System.out.println(num);
    }

    //多参数
    @ParameterizedTest
    @CsvSource({"小明,10,一班","小李,11,二班"})//多参数使用@CsvSource注解实现,这里传入了两组数据,分别传入中间使用逗号隔开
    public void MultParams(String name,int age,String className ){
        System.out.println("name:"+name+" age:"+age+" classname:"+className);
    }

    @ParameterizedTest
    @CsvFileSource(resources = "/data.csv")//读取csv文件中的信息
    public void ParamByFile(String name, String password) {
        System.out.println("name:" + name + " password:" + password);
    }
}

执行后效果如下:

Junit5 单元测试_测试类_06

七、测试套件

使用 JUnit 5 测试套件,我们可以运行分布到多个测试类和不同包中的测试。JUnit 5 提供了两个注解:@SelectPackages 和@SelectClasses 来创建测试套件。要执行该套件,我们将使用 @RunWith(JUnitPlatform.class)。需要引入依赖如下:

<!--创建测试套件-->
        <dependency>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-suite-api</artifactId>
            <version>1.6.3</version>
        </dependency>
        <!--执行测试套件-->
        <dependency>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-runner</artifactId>
            <version>1.6.3</version>
        </dependency>
  • 执行某个包下面的所有测试用例如下:
@RunWith(JUnitPlatform.class)
@SelectPackages("com.easybuy.test")//加载执行包com.easybuy.test下的所有测试用例
public class JUnit5TestSuiteExample {

}
  • 指定执行某个类下的用例
@RunWith(JUnitPlatform.class)
@SelectClasses({
        com.easybuy.test.ParamsTest.class,
        com.easybuy.test.AppTest.class
})//在测试套件中加载执行ParamsTest和AppTest类
public class JUnit5TestClassSuiteExample {
}

此外,我们可以使用以下注释来过滤测试包、类甚至测试方法。

  • @IncludePackages和 @ExcludePackages过滤包
  • @IncludeClassNamePatterns和 @ExcludeClassNamePatterns来过滤测试类
  • @IncludeTags和 @ExcludeTags来过滤测试方法
@RunWith(JUnitPlatform.class)
@SelectPackages("com.easybuy.test")//加载执行包com.easybuy.test下的所有测试用例
@ExcludeClassNamePatterns("com.easybuy.test.ParamsTest")//排除ParamsTest类
public class JUnit5TestSuiteExample {

}

执行结果如下图:

Junit5 单元测试_测试类_07

八、Junit5结合allure生成测试报告

8.1.下载和安装allrue

由于Junit的测试结果展示过于简陋,可以借助于allure生成可视化的测试报告更加的美观同时易用性也不错,allure是一个通用的测试报告框架,

GitHub下载地址:https://github.com/allure-framework/allure2/releases,进入后选择版本下载即可:

Junit5 单元测试_测试方法_08

 需要java8+,JDK 1.8+环境,所以提前配置好java环境,将安装包解压后配置allure环境变量,在系统变量path中添加到bin,例如:

D:\allure-2.21.0\bin

配置成功后在命令窗口输入allure或者allure --version,可以看到我的allure版本是2.21.0

Junit5 单元测试_测试类_09

8.2.allure集成Junit5依赖导入

在pom.xml中添加依赖,然后进行配置:

<properties>
        <aspectj.version>1.9.5</aspectj.version>
        <allure.version>2.13.2</allure.version>
    </properties>

    <dependencies>
        <!--配置和加载测试计划的公共API – 典型的使用场景是IDE和构建工具-->
        <dependency>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-launcher</artifactId>
            <version>1.7.0</version>
            <scope>test</scope>
        </dependency>

        <!--JUnit Jupiter测试引擎的实现,仅仅在运行时需要。-->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.7.0</version>
            <scope>test</scope>
        </dependency>

        <!--JUnit Vintage测试引擎实现,允许在新的JUnit Platform上运行低版本的JUnit测试,即那些以JUnit 3或JUnit 4风格编写的测试。
        非必须
        -->
        <dependency>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
            <version>5.7.0</version>
            <scope>test</scope>
        </dependency>
        <!--编写测试 和 扩展 的JUnit Jupiter API。-->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>5.7.0</version>
        </dependency>

        <!--参数化-->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-params</artifactId>
            <version>5.7.0</version>
            <scope>compile</scope>
        </dependency>

        <!--创建测试套件-->
        <dependency>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-suite-api</artifactId>
            <version>1.6.3</version>
        </dependency>
        <!--执行测试套件-->
        <dependency>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-runner</artifactId>
            <version>1.6.3</version>
        </dependency>

        <!--SL4J 主要是为了给Java日志访问提供一个标准、规范的API框架,其主要意义在于提供接口,具体的实现可以交由其他日志框架
        我的用例执行的时候console中一直提示缺少,故而添加
        -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-nop</artifactId>
            <version>1.7.2</version>
        </dependency>

        <!--Junit5集成allure-->
        <dependency>
            <groupId>io.qameta.allure</groupId>
            <artifactId>allure-junit5</artifactId>
            <version>2.13.2</version>
        </dependency>

    </dependencies>

    <!--junit allure maven运行测试用例jar包-->
    <build>
        <plugins>
            <!--junit allure maven运行测试用例jar包-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.2</version>
                <configuration>
                    <argLine>
                        -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
                    </argLine>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjweaver</artifactId>
                        <version>${aspectj.version}</version>
                    </dependency>
                </dependencies>
            </plugin>
            <plugin>
                <groupId>io.qameta.allure</groupId>
                <artifactId>allure-maven</artifactId>
                <version>2.10.0</version>
                <configuration>
                    <reportVersion>${allure.version}</reportVersion>
                    <allureDownloadUrl>/maven2/io/qameta/allure/allure-commandline/${allure.version}/allure-commandline-${allure.version}.zip</allureDownloadUrl>
                </configuration>
            </plugin>
        </plugins>
    </build>

上面会设置后。执行用例(可以根据测试套件执行,也可以执行某个模块还是跟之前一样)生成的结果以josn格式存放在allure-results在根目录下:

Junit5 单元测试_测试方法_10

8.3.生成allure测试报告

8.3.1.生成在线的测试报告

生成在线版本的测试报告,在命令行打开执行即可

allure serve allure-results

如下图:

Junit5 单元测试_测试类_11

8.3.1.生成文件持久型测试报告

先使用 allure generate 生成htl格式的测试结果报告

allure generate ./allure-results -o ./fileresult --clean

注意:上面的命令将 ./allure-results 目录下的测试数据生成html测试报告到 ./fileresult 路径下。–clean 先清空测试报告目录,再生成新的测试报告。

打开测试报告:

allure open -h 127.0.0.1 -p 8885 ./fileresult

启动一个web服务,将已经生成的html测试报告在默认的浏览器中打开,地址为:http://localhost:8885/

Junit5 单元测试_测试类_12

在浏览器打开测试报告如下:

Junit5 单元测试_测试方法_13