欢送拜访我的GitHub

https://github.com/zq2599/blog_demos

内容:所有原创文章分类汇总及配套源码,波及Java、Docker、Kubernetes、DevOPS等;

对于《JUnit5学习》系列

《JUnit5学习》系列旨在通过实战晋升SpringBoot环境下的单元测试技能,一共八篇文章,链接如下:

  1. 基本操作
  2. Assumptions类
  3. Assertions类
  4. 按条件执行
  5. 标签(Tag)和自定义注解
  6. 参数化测试(Parameterized Tests)根底
  7. 参数化测试(Parameterized Tests)进阶
  8. 综合进阶(终篇)

本篇概览

本文是《JUnit5学习》系列的第一篇,通过实战学习在SpringBoot框架下JUnit5的基本功能,全篇章节如下:

  1. JUnit5简介
  2. SpringBoot对JUnit5的依赖
  3. 罕用注解简介
  4. 5版本已废除的注解介绍
  5. 进入实战环节,先介绍版本和环境信息
  6. 创立《JUnit5学习》系列源码的父工程
  7. 创立子工程,编码体验罕用注解

对于JUnit5

  1. JUnit是罕用的java单元测试框架,<font color="blue">5</font>是以后最新版本,其整体架构如下(图片来自网络):

  1. 从上图可见,整个JUnit5能够划分成三层:顶层框架(Framework)、两头的引擎(Engine),底层的平台(Platform);
  2. 官网定义JUnit5由三局部组成:Platform、Jupiter、Vintage,性能如下;
  3. Platform:位于架构的最底层,是JVM上执行单元测试的根底平台,还对接了各种IDE(例如IDEA、eclipse),并且还与引擎层对接,定义了引擎层对接的API;
  4. Jupiter:位于引擎层,反对5版本的编程模型、扩大模型;
  5. Vintage:位于引擎层,用于执行低版本的测试用例;
  • 可见整个Junit Platform是凋谢的,通过引擎API各种测试框架都能够接入;

SpringBoot对JUnit5的依赖

  1. 这里应用SpringBoot版本为<font color="blue">2.3.4.RELEASE</font>,在我的项目的pom.xml中依赖JUnit5的办法如下:
<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-test</artifactId>    <scope>test</scope></dependency>
  1. 如下图红框,可见JUnit5的jar都被<font color="blue">spring-boot-starter-test</font>间接依赖进来了:

已经的RunWith注解

  1. 在应用JUnit4的时候,咱们常常这么写单元测试类:
@RunWith(SpringRunner.class)@SpringBootTestpublic class XXXTest {
  1. 对于下面的RunWith注解,JUnit5官网文档的说法如下图红框所示,曾经被<font color="blue">ExtendWith</font>取代:

  1. 咱们再来看看SpringBootTest注解,如下图,可见曾经蕴含了<font color="blue">ExtendWith</font>:

  1. 综上所述,SpringBoot+JUnit5时,RunWith注解曾经不须要了,失常状况下仅SpringBootTest注解即可,如果对扩展性有更多需要,能够增加ExtendWith注解,如下图:

罕用的JUnit5注解(SpringBoot环境)

留神,接下来提到的<font color="red">测试方法</font>,是指以后class中所有被@Test、@RepeatedTest、@ParameterizedTest、@TestFactory润饰的办法;

  1. ExtendWith:这是用来取代旧版本中的RunWith注解,不过在SpringBoot环境如果没有特地要求无需额定配置,因为SpringBootTest中曾经有了;
  2. Test:被该注解润饰的就是测试方法;
  3. BeforeAll:被该注解润饰的必须是静态方法,会在所有测试方法之前执行,会被子类继承,取代低版本的BeforeClass;
  4. AfterAll:被该注解润饰的必须是静态方法,会在所有测试方法执行之后才被执行,会被子类继承,取代低版本的AfterClass;
  5. BeforeEach:被该注解润饰的办法会在每个测试方法执行前被执行一次,会被子类继承,取代低版本的Before;
  6. AfterEach:被该注解润饰的办法会在每个测试方法执行后被执行一次,会被子类继承,取代低版本的Before;
  7. DisplayName:测试方法的展示名称,在测试框架中展现,反对emoji;
  8. Timeout:超时时长,被润饰的办法如果超时则会导致测试不通过;
  9. Disabled:不执行的测试方法;

5版本已废除的注解

以下的注解都是在5之前的版本应用的,当初曾经被废除:

被废除的注解新的继任者
BeforeBeforeEach
AfterAfterEach
BeforeClassBeforeAll
AfterClassAfterAll
CategoryTag
RunWithExtendWith
RuleExtendWith
ClassRuleRegisterExtension

版本和环境信息

整个系列的编码和执行在以下环境进行,供您参考:

  1. 硬件配置:处理器i5-8400,内存32G,硬盘128G SSD + 500G HDD
  2. 操作系统:Windows10家庭中文版
  3. IDEA:2020.2.2 (Ultimate Edition)
  4. JDK:1.8.0_181
  5. SpringBoot:2.3.4.RELEASE
  6. JUnit Jupiter:5.6.2

接下来开始实战,咱们先建好SpringBoot我的项目;

对于lombok

为了简化代码,我的项目中应用了lombok,请您在IDEA中装置lombok插件;

源码下载

  1. 如果您不想编码,能够在GitHub下载所有源码,地址和链接信息如下表所示(https://github.com/zq2599/blo...:
名称链接备注
我的项目主页https://github.com/zq2599/blo...该我的项目在GitHub上的主页
git仓库地址(https)https://github.com/zq2599/blo...该我的项目源码的仓库地址,https协定
git仓库地址(ssh)git@github.com:zq2599/blog_demos.git该我的项目源码的仓库地址,ssh协定
  1. 这个git我的项目中有多个文件夹,本章的利用在<font color="blue">junitpractice</font>文件夹下,如下图红框所示:

  1. <font color="blue">junitpractice</font>是父子构造的工程,本篇的代码在<font color="red">junit5experience</font>子工程中,如下图:

创立Maven父工程

  1. 为了便于管理整个系列的源码,在此建设名为<font color="blue">junitpractice</font>的maven工程,后续所有实战的源码都作为junitpractice的子工程;
  2. junitpractice的pom.xml如下,可见是以SpringBoot的<font color="blue">2.3.4.RELEASE</font>版本作为其父工程:
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <modules>        <module>simplebean</module>        <!--        <module>testenvironment</module>        -->    </modules>    <parent>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-parent</artifactId>        <version>2.3.4.RELEASE</version>        <relativePath/> <!-- lookup parent from repository -->    </parent>    <groupId>com.bolingcavalry</groupId>    <artifactId>junitpractice</artifactId>    <version>1.0-SNAPSHOT</version>    <packaging>pom</packaging>    <properties>        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>        <java.version>1.8</java.version>    </properties>    <dependencyManagement>        <dependencies>            <dependency>                <groupId>org.projectlombok</groupId>                <artifactId>lombok</artifactId>                <version>1.16.16</version>            </dependency>        </dependencies>    </dependencyManagement></project>

本篇的源码工程

接下来咱们筹备一个简略的SpringBoot工程用于做单元测试,该工程有service和controller层,蕴含一些简略的接口和类;

  1. 创立名为<font color="blue">junit5experience</font>的子工程,pom.xml如下,留神单元测试要依赖<font color="blue">spring-boot-starter-test</font>:
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <parent>        <groupId>com.bolingcavalry</groupId>        <artifactId>junitpractice</artifactId>        <version>1.0-SNAPSHOT</version>        <relativePath>../pom.xml</relativePath>    </parent>    <groupId>com.bolingcavalry</groupId>    <artifactId>junit5experience</artifactId>    <version>0.0.1-SNAPSHOT</version>    <name>junit5experience</name>    <description>Demo project for simplebean in Spring Boot junit5</description>    <properties>        <java.version>1.8</java.version>    </properties>    <dependencies>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-web</artifactId>        </dependency>        <dependency>            <groupId>org.projectlombok</groupId>            <artifactId>lombok</artifactId>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-test</artifactId>            <scope>test</scope>        </dependency>        <dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-webflux</artifactId>            <scope>test</scope>        </dependency>    </dependencies>    <build>        <plugins>            <plugin>                <groupId>org.springframework.boot</groupId>                <artifactId>spring-boot-maven-plugin</artifactId>            </plugin>        </plugins>    </build></project>
  1. 写一些最简略的业务代码,首先是service层的接口HelloService.java:
package com.bolingcavalry.junit5experience.service;public interface HelloService {    String hello(String name);    int increase(int value);    /**     * 该办法会期待1秒后返回true,这是在模仿一个耗时的近程调用     * @return     */    boolean remoteRequest();}
  1. 上述接口对应的实现类如下,hello和increase办法别离返回String型和int型,remoteRequest成心sleep了1秒钟,用来测试Timeout注解的成果:
package com.bolingcavalry.junit5experience.service.impl;import com.bolingcavalry.junit5experience.service.HelloService;import org.springframework.stereotype.Service;@Service()public class HelloServiceImpl implements HelloService {    @Override    public String hello(String name) {        return "Hello " + name;    }    @Override    public int increase(int value) {        return value + 1;    }    @Override    public boolean remoteRequest() {        try {            Thread.sleep(1000);        } catch (InterruptedException interruptedException) {            interruptedException.printStackTrace();        }        return true;    }}
  1. 增加一个简略的controller:
package com.bolingcavalry.junit5experience.controller;import com.bolingcavalry.junit5experience.service.HelloService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class HelloController {    @Autowired    private HelloService helloService;    @RequestMapping(value = "/{name}", method = RequestMethod.GET)    public String hello(@PathVariable String name){        return helloService.hello(name);    }}
  1. 启动类:
package com.bolingcavalry.junit5experience;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class Junit5ExperienceApplication {    public static void main(String[] args) {        SpringApplication.run(Junit5ExperienceApplication.class, args);    }}
  • 以上就是一个典型的web工程,接下来一起为该工程编写单元测试用例;

编写测试代码

  1. 在下图红框地位新增单元测试类:

  1. 测试类的内容如下,涵盖了方才提到的罕用注解,请留神每个办法的正文阐明:
package com.bolingcavalry.junit5experience.service.impl;import com.bolingcavalry.junit5experience.service.HelloService;import lombok.extern.slf4j.Slf4j;import org.junit.jupiter.api.*;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import java.util.concurrent.TimeUnit;import static org.assertj.core.api.Assertions.assertThat;@SpringBootTest@Slf4jclass HelloServiceImplTest {    private static final String NAME = "Tom";    @Autowired    HelloService helloService;    /**     * 在所有测试方法执行前被执行     */    @BeforeAll    static void beforeAll() {        log.info("execute beforeAll");    }    /**     * 在所有测试方法执行后被执行     */    @AfterAll    static void afterAll() {        log.info("execute afterAll");    }    /**     * 每个测试方法执行前都会执行一次     */    @BeforeEach    void beforeEach() {        log.info("execute beforeEach");    }    /**     * 每个测试方法执行后都会执行一次     */    @AfterEach    void afterEach() {        log.info("execute afterEach");    }    @Test    @DisplayName("测试service层的hello办法")    void hello() {        log.info("execute hello");        assertThat(helloService.hello(NAME)).isEqualTo("Hello " + NAME);    }    /**     * DisplayName中带有emoji,在测试框架中可能展现     */    @Test    @DisplayName("测试service层的increase办法\uD83D\uDE31")    void increase() {        log.info("execute increase");        assertThat(helloService.increase(1)).isEqualByComparingTo(2);    }    /**     * 不会被执行的测试方法     */    @Test    @Disabled    void neverExecute() {        log.info("execute neverExecute");    }    /**     * 调用一个耗时1秒的办法,用Timeout设置超时工夫是500毫秒,     * 因而该用例会测试失败     */    @Test    @Timeout(unit = TimeUnit.MILLISECONDS, value = 500)    @Disabled    void remoteRequest() {        assertThat(helloService.remoteRequest()).isEqualTo(true);    }}
  1. 接下来执行测试用例试试,点击下图红框中的按钮:

  1. 如下图,在弹出的菜单中,点击红框地位:

  1. 执行后果如下,可见Displayname注解的值作为测试后果的办法名展现,超时的办法会被断定为测试不通过,Disable注解润饰的办法则被标记为跳过不执行:

  1. 在父工程junitpractice的pom.xml文件所在目录,执行<font color="blue">mvn test</font>命令,能够看到maven执行单元测试的成果:

  • 至此,咱们对SpringBoot环境下的JUnit5有了最根本的理解,接下来的章节会开展更多知识点和细节,对单元测试做更深刻的学习。

你不孤独,欣宸原创一路相伴

  1. Java系列
  2. Spring系列
  3. Docker系列
  4. kubernetes系列
  5. 数据库+中间件系列
  6. DevOps系列

欢送关注公众号:程序员欣宸

微信搜寻「程序员欣宸」,我是欣宸,期待与您一起畅游Java世界...
https://github.com/zq2599/blog_demos