歡迎訪問我的GitHub
https://github.com/zq2599/blog_demos
內容:所有原創文章分類匯總及配套原始碼,涉及Java、Docker、Kubernetes、DevOPS等;
關于《JUnit5學習》系列
《JUnit5學習》系列旨在通過實戰提升SpringBoot環境下的單元測驗技能,一共八篇文章,鏈接如下:
- 基本操作
- Assumptions類
- Assertions類
- 按條件執行
- 標簽(Tag)和自定義注解
- 引數化測驗(Parameterized Tests)基礎
- 引數化測驗(Parameterized Tests)進階
- 綜合進階(終篇)
本篇概覽
本文是《JUnit5學習》系列的第五篇,一起來學習JUnit5的標簽(Tag)功能,設想一個工程中的有很多測驗類和測驗方法,有的場景只需執行其中一部分測驗方法,如何實作呢?此時Junit的標簽功能就派上用場了,咱們可以按需要給測驗類或者方法打標簽,在執行單元測驗時按照標簽進行過濾,學完了標簽再來了解JUnit5對自定義注解的支持情況,本篇大綱如下:
- 設定標簽
- 在IDEA中做標簽過濾
- 用maven命令時做標簽過濾
- 用surefire插件時做標簽過濾
- 標簽運算式
- 自定義注解
- 更加簡化的自定義注解
- 標簽命名規范
原始碼下載
- 如果您不想編碼,可以在GitHub下載所有原始碼,地址和鏈接資訊如下表所示:
| 名稱 | 鏈接 | 備注 |
|---|---|---|
| 專案主頁 | 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是父子結構的工程,本篇的代碼在tag子工程中,如下圖:

設定標簽
- 在父工程junitpractice里新建名為tag的子工程,今天的單元測驗代碼都寫在這個tag工程中;
- 一共寫兩個測驗類,第一個FirstTest.java如下,可見類上有Tag注解,值為first,另外每個方法上都有Tag注解,其中first1Test方法有兩個Tag注解:
package com.bolingcavalry.tag.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.assertEquals;
@SpringBootTest
@Slf4j
@Tag("first")
public class FirstTest {
@Test
@Tag("easy")
@Tag("important")
@DisplayName("first-1")
void first1Test() {
log.info("first1Test");
assertEquals(2, Math.addExact(1, 1));
}
@Test
@Tag("easy")
@DisplayName("first-2")
void first2Test() {
log.info("first2Test");
assertEquals(2, Math.addExact(1, 1));
}
@Test
@Tag("hard")
@DisplayName("first-3")
void first3Test() {
log.info("first3Test");
assertEquals(2, Math.addExact(1, 1));
}
}
- 第二個測驗類SecondTest.java,也是類和方法都有Tag注解:
package com.bolingcavalry.tag.service.impl;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.assertEquals;
@SpringBootTest
@Slf4j
@Tag("second")
public class SecondTest {
@Test
@Tag("easy")
@DisplayName("second-1")
void second1Test() {
log.info("second1Test");
assertEquals(2, Math.addExact(1, 1));
}
@Test
@Tag("easy")
@DisplayName("second-2")
void second2Test() {
log.info("second2Test");
assertEquals(2, Math.addExact(1, 1));
}
@Test
@Tag("hard")
@Tag("important")
@DisplayName("second-3")
void second3Test() {
log.info("second3Test");
assertEquals(2, Math.addExact(1, 1));
}
}
- 以上就是打好了標簽的測驗類和測驗方法了,接下來看看如何通過這些標簽對測驗方法進行過濾,執行單元測驗有三種常用方式,咱們挨個嘗試每種方式如何用標簽過濾;
在IDEA中做標簽過濾
- 如下圖所示,點擊紅框中的Edit Configurations...:

2. 如下圖紅框,在彈出的視窗上新增一個JUnit配置:

3. 接下來的操作如下圖所示,Test kind選擇Tags,就會按照標簽過濾測驗方法,Tag expression里面填寫過濾規則,后面會詳細講解這個規則,這里先填個已存在的標簽important:

4. 創建好JUnit配置后,執行下圖紅框中的操作即可執行單元測驗:

- 執行結果如下,所有打了important標簽的測驗方法被執行:

用maven命令時做標簽過濾
- 前面試過IDEA上按標簽過濾測驗方法,其實用maven命令執行單元測驗的時候也能按標簽來過濾,接下來試試;
- 在父工程junitpractice的pom.xml所在目錄下,執行以下命令,即可開始單元測驗,并且只執行帶有標簽的方法:
mvn clean test -Dgroups="important"
- 執行完畢后結果如下:

4. 翻看日志,可見只有打了important標簽的測驗方法被執行了,如下圖紅框所示:

- 再看看其他子工程的執行情況,用前一篇文章里的conditional為例,可見沒有任何測驗方法被執行,如下圖紅框所示:

-
再去看看surefire插件給出的測驗報告,報告檔案在junitpractice\tag\target\surefire-reports目錄下,下圖紅框中的檔案就是測驗報告:

-
打開上圖紅框中的一個檔案,如下圖紅框,可見只有打了important標簽的測驗方法被執行了:

- 以上就是maven命令執行單元測驗時使用標簽過濾的方法,接下來試試在使用maven-surefire-plugin插件時如何通過做標簽過濾
用surefire插件時做標簽過濾
- surefire是個測驗引擎(TestEngine),以maven插件的方式來使用,打開tag子工程的pom.xml檔案,將build節點配置成以下形式,可見groups就是標簽過濾節點,另外excludedGroups節點制定的hard標簽的測驗方法不會執行:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
<configuration>
<!--要執行的標簽-->
<groups>important</groups>
<!--不要執行的標簽-->
<excludedGroups>hard</excludedGroups>
</configuration>
</plugin>
</plugins>
</build>
- 在tag子工程的pom.xml所在目錄,執行命令mvn clean test即可開始單元測驗,結果如下,可見打了important標簽的first1Test被執行,而second3Test方法盡管有important標簽,但是由于其hard標簽已經被設定為不執行,因此second3Test沒有被執行:

標簽運算式
- 前面咱們用三種方法執行了單元測驗,每次都是用important標簽過濾,其實除了指定標簽,JUnit還支持更復雜的標簽過濾,即標簽運算式
- 所謂標簽運算式,就是用"非"、"與"、"或"這三種運算子將更多的標簽連接起來,實作更復雜的過濾邏輯;
- 上述三種運算子的定義和用法如下表:
| 運算子 | 作用 | 舉例 | 舉例說明 |
|---|---|---|---|
| & | 與 | important & easy | 既有important,又有easy標簽, 在本文是first1Test |
| ! | 非 | important & !easy | 有important,同時又沒有easy標簽, 在本文是second3Test |
| | | 或 | important | hard | 有important標簽的,再加上有hard標簽的, 在本文是first1Test、first3Test、second3Test |
- 試試標簽運算式的效果,如下圖紅框,修改前面創建好的IDEA配置,從之前的important改為important | hard:

5. 再次執行這個配置,結果如下圖紅框所示,只有這三個方法被執行:first1Test、first3Test、second3Test,可見標簽運算式生效了:

6. 在maven命令和surefire插件中使用標簽運算式的操作就不在文中執行了,請您自行驗證;
自定義注解
- JUnit支持自定義注解,先回顧之前的代碼,看咱們是如何給方法打標簽的,以first3Test方法為例:
@Test
@Tag("hard")
@DisplayName("first-3")
void first3Test() {
log.info("first3Test");
assertEquals(2, Math.addExact(1, 1));
}
- 接下來咱們創建一個注解,將@Tag("hard")替換掉,新注解的原始碼如下,可見僅是一個普通的注解定義:
package com.bolingcavalry.tag.service.impl;
import org.junit.jupiter.api.Tag;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Tag("hard")
public @interface Hard {
}
- 修改first3Test方法的注解,去掉@Tag("hard"),改為@Hard:
@Test
@Hard
@DisplayName("first-3")
void first3Test() {
log.info("first3Test");
assertEquals(2, Math.addExact(1, 1));
}
- 執行前面創建的tag-important配置,可見hard標簽的過濾依舊有效:

更加簡化的自定義注解
- 上述Hard注解取代了@Tag("hard"),其實還可以更進一步對已有注解做簡化,下面是個新的注解:HardTest.java,和Hard.java相比,多了個@Test,作用是集成了Test注解的能力
package com.bolingcavalry.tag.service.impl;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Tag("hard")
@Test
public @interface HardTest {
}
- 于是,first3Test方法的注解可以改成下面的效果,可見Test和Tag注解都去掉了:
@HardTest
@DisplayName("first-3")
void first3Test() {
log.info("first3Test");
assertEquals(2, Math.addExact(1, 1));
}
- 執行前面創建的tag-important配置,可見hard標簽的過濾依舊有效:

標簽命名規范
最后一起來看看給標簽取名時有哪些要注意的地方:
- 標簽名左右兩側的空格是無效的,執行測驗的時候會做trim處理,例如下面這個標簽會被當作hard來過濾:

2. 標簽名不能有這六個符號, ( ) & | !
- 至此,JUnit5的標簽過濾和自定義注解功能都學習完成了,有了這些能力,咱們可以更加靈活和隨心所欲的應付不同的場景和需求;
你不孤單,欣宸原創一路相伴
- Java系列
- Spring系列
- Docker系列
- kubernetes系列
- 資料庫+中間件系列
- DevOps系列
歡迎關注公眾號:程式員欣宸
微信搜索「程式員欣宸」,我是欣宸,期待與您一同暢游Java世界...
https://github.com/zq2599/blog_demos
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/264052.html
標籤:Java
上一篇:HTTPS:網路安全攻堅戰
