欢送拜访我的 GitHub
https://github.com/zq2599/blog_demos
内容:所有原创文章分类汇总及配套源码,波及 Java、Docker、Kubernetes、DevOPS 等;
对于《JUnit5 学习》系列
《JUnit5 学习》系列旨在通过实战晋升 SpringBoot 环境下的单元测试技能,一共八篇文章,链接如下:
- 基本操作
- Assumptions 类
- Assertions 类
- 按条件执行
- 标签 (Tag) 和自定义注解
- 参数化测试 (Parameterized Tests) 根底
- 参数化测试 (Parameterized Tests) 进阶
- 综合进阶(终篇)
本篇概览
本文是《JUnit5 学习》系列的第一篇,通过实战学习在 SpringBoot 框架下 JUnit5 的基本功能,全篇章节如下:
- JUnit5 简介
- SpringBoot 对 JUnit5 的依赖
- 罕用注解简介
- 5 版本已废除的注解介绍
- 进入实战环节,先介绍版本和环境信息
- 创立《JUnit5 学习》系列源码的父工程
- 创立子工程,编码体验罕用注解
对于 JUnit5
- JUnit 是罕用的 java 单元测试框架,<font color=”blue”>5</font> 是以后最新版本,其整体架构如下(图片来自网络):
- 从上图可见,整个 JUnit5 能够划分成三层:顶层框架(Framework)、两头的引擎(Engine),底层的平台(Platform);
- 官网定义 JUnit5 由三局部组成:Platform、Jupiter、Vintage,性能如下;
- Platform:位于架构的最底层,是 JVM 上执行单元测试的根底平台,还对接了各种 IDE(例如 IDEA、eclipse),并且还与引擎层对接,定义了引擎层对接的 API;
- Jupiter:位于引擎层,反对 5 版本的编程模型、扩大模型;
- Vintage:位于引擎层,用于执行低版本的测试用例;
- 可见整个 Junit Platform 是凋谢的,通过引擎 API 各种测试框架都能够接入;
SpringBoot 对 JUnit5 的依赖
- 这里应用 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>
- 如下图红框,可见 JUnit5 的 jar 都被 <font color=”blue”>spring-boot-starter-test</font> 间接依赖进来了:
已经的 RunWith 注解
- 在应用 JUnit4 的时候,咱们常常这么写单元测试类:
@RunWith(SpringRunner.class)
@SpringBootTest
public class XXXTest {
- 对于下面的 RunWith 注解,JUnit5 官网文档的说法如下图红框所示,曾经被 <font color=”blue”>ExtendWith</font> 取代:
- 咱们再来看看 SpringBootTest 注解,如下图,可见曾经蕴含了 <font color=”blue”>ExtendWith</font>:
- 综上所述,SpringBoot+JUnit5 时,RunWith 注解曾经不须要了,失常状况下仅 SpringBootTest 注解即可,如果对扩展性有更多需要,能够增加 ExtendWith 注解,如下图:
罕用的 JUnit5 注解(SpringBoot 环境)
留神,接下来提到的 <font color=”red”> 测试方法 </font>,是指以后 class 中所有被 @Test、@RepeatedTest、@ParameterizedTest、@TestFactory 润饰的办法;
- ExtendWith:这是用来取代旧版本中的 RunWith 注解,不过在 SpringBoot 环境如果没有特地要求无需额定配置,因为 SpringBootTest 中曾经有了;
- Test:被该注解润饰的就是测试方法;
- BeforeAll:被该注解润饰的必须是静态方法,会在所有测试方法之前执行,会被子类继承,取代低版本的 BeforeClass;
- AfterAll:被该注解润饰的必须是静态方法,会在所有测试方法执行之后才被执行,会被子类继承,取代低版本的 AfterClass;
- BeforeEach:被该注解润饰的办法会在每个测试方法执行前被执行一次,会被子类继承,取代低版本的 Before;
- AfterEach:被该注解润饰的办法会在每个测试方法执行后被执行一次,会被子类继承,取代低版本的 Before;
- DisplayName:测试方法的展示名称,在测试框架中展现,反对 emoji;
- Timeout:超时时长,被润饰的办法如果超时则会导致测试不通过;
- Disabled:不执行的测试方法;
5 版本已废除的注解
以下的注解都是在 5 之前的版本应用的,当初曾经被废除:
被废除的注解 | 新的继任者 |
---|---|
Before | BeforeEach |
After | AfterEach |
BeforeClass | BeforeAll |
AfterClass | AfterAll |
Category | Tag |
RunWith | ExtendWith |
Rule | ExtendWith |
ClassRule | RegisterExtension |
版本和环境信息
整个系列的编码和执行在以下环境进行,供您参考:
- 硬件配置:处理器 i5-8400,内存 32G,硬盘 128G SSD + 500G HDD
- 操作系统:Windows10 家庭中文版
- IDEA:2020.2.2 (Ultimate Edition)
- JDK:1.8.0_181
- SpringBoot:2.3.4.RELEASE
- JUnit Jupiter:5.6.2
接下来开始实战,咱们先建好 SpringBoot 我的项目;
对于 lombok
为了简化代码,我的项目中应用了 lombok,请您在 IDEA 中装置 lombok 插件;
源码下载
- 如果您不想编码,能够在 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 协定 |
- 这个 git 我的项目中有多个文件夹,本章的利用在 <font color=”blue”>junitpractice</font> 文件夹下,如下图红框所示:
- <font color=”blue”>junitpractice</font> 是父子构造的工程,本篇的代码在 <font color=”red”>junit5experience</font> 子工程中,如下图:
创立 Maven 父工程
- 为了便于管理整个系列的源码,在此建设名为 <font color=”blue”>junitpractice</font> 的 maven 工程,后续所有实战的源码都作为 junitpractice 的子工程;
- 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 层,蕴含一些简略的接口和类;
- 创立名为 <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>
- 写一些最简略的业务代码,首先是 service 层的接口 HelloService.java:
package com.bolingcavalry.junit5experience.service;
public interface HelloService {String hello(String name);
int increase(int value);
/**
* 该办法会期待 1 秒后返回 true,这是在模仿一个耗时的近程调用
* @return
*/
boolean remoteRequest();}
- 上述接口对应的实现类如下,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;
}
}
- 增加一个简略的 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;
@RestController
public class HelloController {
@Autowired
private HelloService helloService;
@RequestMapping(value = "/{name}", method = RequestMethod.GET)
public String hello(@PathVariable String name){return helloService.hello(name);
}
}
- 启动类:
package com.bolingcavalry.junit5experience;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Junit5ExperienceApplication {public static void main(String[] args) {SpringApplication.run(Junit5ExperienceApplication.class, args);
}
}
- 以上就是一个典型的 web 工程,接下来一起为该工程编写单元测试用例;
编写测试代码
- 在下图红框地位新增单元测试类:
- 测试类的内容如下,涵盖了方才提到的罕用注解,请留神每个办法的正文阐明:
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
@Slf4j
class 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);
}
}
- 接下来执行测试用例试试,点击下图红框中的按钮:
- 如下图,在弹出的菜单中,点击红框地位:
- 执行后果如下,可见 Displayname 注解的值作为测试后果的办法名展现,超时的办法会被断定为测试不通过,Disable 注解润饰的办法则被标记为跳过不执行:
- 在父工程 junitpractice 的 pom.xml 文件所在目录,执行 <font color=”blue”>mvn test</font> 命令,能够看到 maven 执行单元测试的成果:
- 至此,咱们对 SpringBoot 环境下的 JUnit5 有了最根本的理解,接下来的章节会开展更多知识点和细节,对单元测试做更深刻的学习。
你不孤独,欣宸原创一路相伴
- Java 系列
- Spring 系列
- Docker 系列
- kubernetes 系列
- 数据库 + 中间件系列
- DevOps 系列
欢送关注公众号:程序员欣宸
微信搜寻「程序员欣宸」,我是欣宸,期待与您一起畅游 Java 世界 …
https://github.com/zq2599/blog_demos