歡迎訪問我的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單元測驗框架,5是當前最新版本,其整體架構如下(圖片來自網路):

2. 從上圖可見,整個JUnit5可以劃分成三層:頂層框架(Framework)、中間的引擎(Engine),底層的平臺(Platform);
3. 官方定義JUnit5由三部分組成:Platform、Jupiter、Vintage,功能如下;
4. Platform:位于架構的最底層,是JVM上執行單元測驗的基礎平臺,還對接了各種IDE(例如IDEA、eclipse),并且還與引擎層對接,定義了引擎層對接的API;
5. Jupiter:位于引擎層,支持5版本的編程模型、擴展模型;
6. Vintage:位于引擎層,用于執行低版本的測驗用例;
- 可見整個Junit Platform是開放的,通過引擎API各種測驗框架都可以接入;
SpringBoot對JUnit5的依賴
- 這里使用SpringBoot版本為2.3.4.RELEASE,在專案的pom.xml中依賴JUnit5的方法如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
- 如下圖紅框,可見JUnit5的jar都被spring-boot-starter-test間接依賴進來了:

曾經的RunWith注解
- 在使用JUnit4的時候,咱們經常這么寫單元測驗類:
@RunWith(SpringRunner.class)
@SpringBootTest
public class XXXTest {
- 對于上面的RunWith注解,JUnit5官方檔案的說法如下圖紅框所示,已經被ExtendWith取代:

3. 咱們再來看看SpringBootTest注解,如下圖,可見已經包含了ExtendWith:

4. 綜上所述,SpringBoot+JUnit5時,RunWith注解已經不需要了,正常情況下僅SpringBootTest注解即可,如果對擴展性有更多需求,可以添加ExtendWith注解,如下圖:

常用的JUnit5注解(SpringBoot環境)
注意,接下來提到的測驗方法,是指當前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/blog_demos):
| 名稱 | 鏈接 | 備注 |
|---|---|---|
| 專案主頁 | https://github.com/zq2599/blog_demos | 該專案在GitHub上的主頁 |
| git倉庫地址(https) | https://github.com/zq2599/blog_demos.git | 該專案原始碼的倉庫地址,https協議 |
| git倉庫地址(ssh) | [email protected]:zq2599/blog_demos.git | 該專案原始碼的倉庫地址,ssh協議 |
- 這個git專案中有多個檔案夾,本章的應用在junitpractice檔案夾下,如下圖紅框所示:

- junitpractice是父子結構的工程,本篇的代碼在junit5experience子工程中,如下圖:

創建Maven父工程
- 為了便于管理整個系列的原始碼,在此建立名為junitpractice的maven工程,后續所有實戰的原始碼都作為junitpractice的子工程;
- junitpractice的pom.xml如下,可見是以SpringBoot的2.3.4.RELEASE版本作為其父工程:
<?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層,包含一些簡單的介面和類;
- 創建名為junit5experience的子工程,pom.xml如下,注意單元測驗要依賴spring-boot-starter-test:
<?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 = "https://www.cnblogs.com/{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工程,接下來一起為該工程撰寫單元測驗用例;
撰寫測驗代碼
- 在下圖紅框位置新增單元測驗類:

2. 測驗類的內容如下,涵蓋了剛才提到的常用注解,請注意每個方法的注釋說明:
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 = https://www.cnblogs.com/bolingcavalry/p/500)
@Disabled
void remoteRequest() {
assertThat(helloService.remoteRequest()).isEqualTo(true);
}
}
- 接下來執行測驗用例試試,點擊下圖紅框中的按鈕:

4. 如下圖,在彈出的選單中,點擊紅框位置:

- 執行結果如下,可見Displayname注解的值作為測驗結果的方法名展示,超時的方法會被判定為測驗不通過,Disable注解修飾的方法則被標記為跳過不執行:

- 在父工程junitpractice的pom.xml檔案所在目錄,執行mvn test命令,可以看到maven執行單元測驗的效果:

- 至此,咱們對SpringBoot環境下的JUnit5有了最基本的了解,接下來的章節會展開更多知識點和細節,對單元測驗做更深入的學習,
你不孤單,欣宸原創一路相伴
- Java系列
- Spring系列
- Docker系列
- kubernetes系列
- 資料庫+中間件系列
- DevOps系列
歡迎關注公眾號:程式員欣宸
微信搜索「程式員欣宸」,我是欣宸,期待與您一同暢游Java世界...
https://github.com/zq2599/blog_demos
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/261236.html
標籤:Java
