1. 程式人生 > >JUnit5學習之五:標籤(Tag)和自定義註解

JUnit5學習之五:標籤(Tag)和自定義註解

### 歡迎訪問我的GitHub [https://github.com/zq2599/blog_demos](https://github.com/zq2599/blog_demos) 內容:所有原創文章分類彙總及配套原始碼,涉及Java、Docker、Kubernetes、DevOPS等; ### 關於《JUnit5學習》系列 《JUnit5學習》系列旨在通過實戰提升SpringBoot環境下的單元測試技能,一共八篇文章,連結如下: 1. [基本操作](https://blog.csdn.net/boling_cavalry/article/details/108810587) 2. [Assumptions類](https://blog.csdn.net/boling_cavalry/article/details/108861185) 3. [Assertions類](https://blog.csdn.net/boling_cavalry/article/details/108899437) 4. [按條件執行](https://blog.csdn.net/boling_cavalry/article/details/108909107) 5. [標籤(Tag)和自定義註解](https://blog.csdn.net/boling_cavalry/article/details/108914091) 6. [引數化測試(Parameterized Tests)基礎](https://blog.csdn.net/boling_cavalry/article/details/108930987) 7. [引數化測試(Parameterized Tests)進階](https://blog.csdn.net/boling_cavalry/article/details/108942301) 8. [綜合進階(終篇)](https://blog.csdn.net/boling_cavalry/article/details/108952500) ### 本篇概覽 本文是《JUnit5學習》系列的第五篇,一起來學習JUnit5的標籤(Tag)功能,設想一個工程中的有很多測試類和測試方法,有的場景只需執行其中一部分測試方法,如何實現呢?此時Junit的標籤功能就派上用場了,咱們可以按需要給測試類或者方法打標籤,在執行單元測試時按照標籤進行過濾,學完了標籤再來了解JUnit5對自定義註解的支援情況,本篇大綱如下: 1. 設定標籤 2. 在IDEA中做標籤過濾 3. 用maven命令時做標籤過濾 4. 用surefire外掛時做標籤過濾 5. 標籤表示式 6. 自定義註解 7. 更加簡化的自定義註解 8. 標籤命名規範 ### 原始碼下載 1. 如果您不想編碼,可以在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協議 | 2. 這個git專案中有多個資料夾,本章的應用在junitpractice資料夾下,如下圖紅框所示: ![在這裡插入圖片描述](https://img2020.cnblogs.com/other/485422/202102/485422-20210226081059464-1621455016.jpg) 3. junitpractice是父子結構的工程,本篇的程式碼在tag子工程中,如下圖: ![在這裡插入圖片描述](https://img2020.cnblogs.com/other/485422/202102/485422-20210226081100205-2054219960.jpg) ### 設定標籤 1. 在父工程junitpractice裡新建名為tag的子工程,今天的單元測試程式碼都寫在這個tag工程中; 2. 一共寫兩個測試類,第一個FirstTest.java如下,可見類上有Tag註解,值為first,另外每個方法上都有Tag註解,其中first1Test方法有兩個Tag註解: ```java 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)); } } ``` 3. 第二個測試類SecondTest.java,也是類和方法都有Tag註解: ```java 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中做標籤過濾 1. 如下圖所示,點選紅框中的Edit Configurations...: ![在這裡插入圖片描述](https://img2020.cnblogs.com/other/485422/202102/485422-20210226081101277-1844200790.jpg) 2. 如下圖紅框,在彈出的視窗上新增一個JUnit配置: ![在這裡插入圖片描述](https://img2020.cnblogs.com/other/485422/202102/485422-20210226081102135-447020268.jpg) 3. 接下來的操作如下圖所示,Test kind選擇Tags,就會按照標籤過濾測試方法,Tag expression裡面填寫過濾規則,後面會詳細講解這個規則,這裡先填個已存在的標籤important: ![在這裡插入圖片描述](https://img2020.cnblogs.com/other/485422/202102/485422-20210226081102728-1005010334.jpg) 4. 建立好JUnit配置後,執行下圖紅框中的操作即可執行單元測試: ![在這裡插入圖片描述](https://img2020.cnblogs.com/other/485422/202102/485422-20210226081103270-1014587511.jpg) 5. 執行結果如下,所有打了important標籤的測試方法被執行: ![在這裡插入圖片描述](https://img2020.cnblogs.com/other/485422/202102/485422-20210226081103531-749351112.jpg) ### 用maven命令時做標籤過濾 1. 前面試過IDEA上按標籤過濾測試方法,其實用maven命令執行單元測試的時候也能按標籤來過濾,接下來試試; 2. 在父工程junitpractice的pom.xml所在目錄下,執行以下命令,即可開始單元測試,並且只執行帶有標籤的方法: ```shell mvn clean test -Dgroups="important" ``` 3. 執行完畢後結果如下: ![在這裡插入圖片描述](https://img2020.cnblogs.com/other/485422/202102/485422-20210226081105538-121910334.jpg) 4. 翻看日誌,可見只有打了important標籤的測試方法被執行了,如下圖紅框所示: ![在這裡插入圖片描述](https://img2020.cnblogs.com/other/485422/202102/485422-20210226081113586-1832303402.jpg) 5. 再看看其他子工程的執行情況,用前一篇文章裡的conditional為例,可見沒有任何測試方法被執行,如下圖紅框所示: ![在這裡插入圖片描述](https://img2020.cnblogs.com/other/485422/202102/485422-20210226081114100-96325716.jpg) 6. 再去看看surefire外掛給出的測試報告,報告檔案在junitpractice\tag\target\surefire-reports目錄下,下圖紅框中的檔案就是測試報告: ![在這裡插入圖片描述](https://img2020.cnblogs.com/other/485422/202102/485422-20210226081114567-1161523004.jpg) 7. 開啟上圖紅框中的一個檔案,如下圖紅框,可見只有打了important標籤的測試方法被執行了: ![在這裡插入圖片描述](https://img2020.cnblogs.com/other/485422/202102/485422-20210226081115174-735136073.jpg) - 以上就是maven命令執行單元測試時使用標籤過濾的方法,接下來試試在使用maven-surefire-plugin外掛時如何通過做標籤過濾 ### 用surefire外掛時做標籤過濾 1. surefire是個測試引擎(TestEngine),以maven外掛的方式來使用,開啟tag子工程的pom.xml檔案,將build節點配置成以下形式,可見groups就是標籤過濾節點,另外excludedGroups節點制定的hard標籤的測試方法不會執行: ```xml ``` 2. 在tag子工程的pom.xml所在目錄,執行命令mvn clean test即可開始單元測試,結果如下,可見打了important標籤的first1Test被執行,而second3Test方法儘管有important標籤,但是由於其hard標籤已經被設定為不執行,因此second3Test沒有被執行: ![在這裡插入圖片描述](https://img2020.cnblogs.com/other/485422/202102/485422-20210226081118111-908913262.jpg) ### 標籤表示式 1. 前面咱們用三種方法執行了單元測試,每次都是用important標籤過濾,其實除了指定標籤,JUnit還支援更復雜的標籤過濾,即標籤表示式 2. 所謂標籤表示式,就是用"非"、"與"、"或"這三種操作符將更多的標籤連線起來,實現更復雜的過濾邏輯; 3. 上述三種操作符的定義和用法如下表: | 操作符 | 作用 | 舉例 | 舉例說明 | |--|--|--|--| | & | 與 | important & easy | 既有important,又有easy標籤,
在本文是first1Test | | ! | 非 | important & !easy | 有important,同時又沒有easy標籤,
在本文是second3Test | | \| | 或 | important \| hard | 有important標籤的,再加上有hard標籤的,
在本文是first1Test、first3Test、second3Test | 4. 試試標籤表示式的效果,如下圖紅框,修改前面建立好的IDEA配置,從之前的important改為important | hard: ![在這裡插入圖片描述](https://img2020.cnblogs.com/other/485422/202102/485422-20210226081118687-626511235.jpg) 5. 再次執行這個配置,結果如下圖紅框所示,只有這三個方法被執行:first1Test、first3Test、second3Test,可見標籤表示式生效了: ![在這裡插入圖片描述](https://img2020.cnblogs.com/other/485422/202102/485422-20210226081120611-401794736.jpg) 6. 在maven命令和surefire外掛中使用標籤表示式的操作就不在文中執行了,請您自行驗證; ### 自定義註解 1. JUnit支援自定義註解,先回顧之前的程式碼,看咱們是如何給方法打標籤的,以first3Test方法為例: ```java @Test @Tag("hard") @DisplayName("first-3") void first3Test() { log.info("first3Test"); assertEquals(2, Math.addExact(1, 1)); } ``` 2. 接下來咱們建立一個註解,將@Tag("hard")替換掉,新註解的原始碼如下,可見僅是一個普通的註解定義: ```java 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 { } ``` 3. 修改first3Test方法的註解,去掉@Tag("hard"),改為@Hard: ```java @Test @Hard @DisplayName("first-3") void first3Test() { log.info("first3Test"); assertEquals(2, Math.addExact(1, 1)); } ``` 4. 執行前面建立的tag-important配置,可見hard標籤的過濾依舊有效: ![在這裡插入圖片描述](https://img2020.cnblogs.com/other/485422/202102/485422-20210226081122751-133040343.jpg) ### 更加簡化的自定義註解 1. 上述Hard註解取代了@Tag("hard"),其實還可以更進一步對已有註解做簡化,下面是個新的註解:HardTest.java,和Hard.java相比,多了個@Test,作用是集成了Test註解的能力 ```java 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 { } ``` 2. 於是,first3Test方法的註解可以改成下面的效果,可見Test和Tag註解都去掉了: ```java @HardTest @DisplayName("first-3") void first3Test() { log.info("first3Test"); assertEquals(2, Math.addExact(1, 1)); } ``` 3. 執行前面建立的tag-important配置,可見hard標籤的過濾依舊有效: ![在這裡插入圖片描述](https://img2020.cnblogs.com/other/485422/202102/485422-20210226081125077-1410386043.jpg) ### 標籤命名規範 最後一起來看看給標籤取名時有哪些要注意的地方: 1. 標籤名左右兩側的空格是無效的,執行測試的時候會做trim處理,例如下面這個標籤會被當作hard來過濾: ![在這裡插入圖片描述](https://img2020.cnblogs.com/other/485422/202102/485422-20210226081129097-307735502.jpg) 2. 標籤名不能有這六個符號, ( ) & | ! - 至此,JUnit5的標籤過濾和自定義註解功能都學習完成了,有了這些能力,咱們可以更加靈活和隨心所欲的應付不同的場景和需求; ### 你不孤單,欣宸原創一路相伴 1. [Java系列](https://xinchen.blog.csdn.net/article/details/105068742) 2. [Spring系列](https://xinchen.blog.csdn.net/article/details/105086498) 3. [Docker系列](https://xinchen.blog.csdn.net/article/details/105086732) 4. [kubernetes系列](https://xinchen.blog.csdn.net/article/details/105086794) 5. [資料庫+中介軟體系列](https://xinchen.blog.csdn.net/article/details/105086850) 6. [DevOps系列](https://xinchen.blog.csdn.net/article/details/105086920) ### 歡迎關注公眾號:程式設計師欣宸 > 微信搜尋「程式設計師欣宸」,我是欣宸,期待與您一同暢遊Java世界... [https://github.com/zq2599/blog_demos](https://github.com/zq2599/blo