JUnit4.11 理論機制 @Theory 完整解讀


最近在研究JUnit4,大部分基礎技術都是通過百度和JUnit的官方wiki學習的,目前最新的發布版本是4.11,結合代碼實踐,發現官方wiki的內容或多或少沒有更新,Theory理論機制章節情況尤為嚴重,不知道這章wiki對應的是第幾版,筆主在4.11版本中是完全跑不通的,因為接口結構已經改變了,而百度出來的博客文檔更是只有Theory的基礎部分,更具實際應用價值的擴展部分完全不見蹤影,本文根據筆主實際編碼總結經驗,詳細講述如何使用4.11版JUnit的Theory理論機制。

ps. 最近發現網上有一小撮別有用心的國人轉載筆主文章時,順手丟羊把筆主文章頭部標注的原文地址惡意馬賽克掉,也沒有在文后貼出轉載地址,為了普及技術筆主就不追究了,這次故意在貼在這里,本文地址:JUnit4.11 理論機制 @Theory 完整解讀

 

筆主下面所使用代碼,僅依賴於 junit-4.11.jar,建議同時導入 hamcrest-core-1.3.jar、hamcrest-library-1.3.jar 以提供完整的assertThat支持

 

簡單介紹Theory

使用注解@Theory取代@Test標記測試方法,可以支持帶形參的測試方法簽名,並使用指定的數據集自動代入進行連續多次測試,雖然暫未看到官方或其他個人的文字表述,但筆主覺得這個機制是參數化測試 Parameterized tests 的優化擴展版(Parameterized tests需要獨占一個測試類,兼容性靈活性都太差了)。

使用@Theory測試方法需要在測試類頭部聲明@RunWith(Theories.class)

 

傳統Theory

Theory的基礎部分,通過顯式預定義各種Class類型變量,在@Theory的帶參測試方法中自動逐次傳入相同Class類型的變量值,運行多次測試(依據預定義變量數量而定),復數形參情況使用預定義變量排列組合出實參對進行代入測試。

 

1、使用@DataPoint標記待用實參數據

1 @DataPoint
2 public static String **1 = “####”;
3 @DataPoint
4 public static String **2 = “####”;
5 @DataPoint
6 public static String **3 = “####”;

 

2.1、使用@Theory標記單形參測試方法

1 @Theory
2 // 僅輸入與形參相同類型(String)的預定義@DataPoint數據,此處會自動輪流代入上面的 **1 - **3 作為實參測試數據,運行3次test1測試
3 public void test1(String ***) {
4         // 使用assume設置過濾
5         assumeThat(***);
6         assertThat(***);
7 }

 

2.2、復數形參測試方法

1 @Theory
2 // 使用排列組合與形參同類型(String)@DataPoint自動化輸入,與命名、順序無關,此處s1、s2將輪流使用 **1 - **3 的排列組合,如s1=**1,s2=**2或s1=**2,s2=**1,運行次數依據排列組合方案數量而定
3 public void test2(String s1, String s2) { 
4         assumeThat(***);
5         assertThat(***);
6 }

 

Theory擴展(Popper project)

這是本文的核心部分,傳統Theory指定實參變量的方式存在非常明顯的局限性,無法精確控制每個形參的可用變量值范圍,因此JUnit引入Popper 項目的技術,提供一種完全自定義指定實參數據集的方法。

 

定制@Theory測試方法實參變量值范圍,主要使用 Parameter Supplier 結構

 

系統自帶默認實現

JUnit中自帶一個默認的 Parameter Supplier 實現:@TestedOn(ints = int[])

使用方式示例:

1 @Theory
2 public final void test(@TestedOn(ints = { 0, 1, 2 }) int i) {
3     assertTrue(i >= 0);
4 }

 

在這個例子中,可以很直觀的看到形參 i 在實際運行測試中,會依次自動代入取值為 ints 指定的數組{0, 1, 2}中每個元素

 

完全自定義實現

JUnit默認只提供了一個int型的簡單 Parameter Supplier 實現,而Theory機制真正的價值在於,能參考@TestedOn的做法,相對簡單的完全自定義出可重用 Parameter Supplier,適應於各種復雜要求的限定范圍參數值測試場景,滿足開發者所需的高度動態自定義范圍取值自動化測試,同時保留與一般@Test相同的強大兼容性,作為本文核心中的戰斗機,下面將通過兩個栗子,詳細描述如何一步步實現自定義 Parameter Supplier。

 

自定義實現I(動態實參值表)

本方式以一個自定義注解接口@Between為例,展示如何通過讀取注解屬性變量值,動態創建相應的實參數據集。

 

1、定義annotation注解接口 Between:

1 @Retention(RetentionPolicy.RUNTIME)
2 // 聲明注解接口所使用的委托處理類
3 @ParametersSuppliedBy(BetweenSupplier.class)
4 public @interface Between{
5     // 聲明所有可用參數,效果為 @Between([first = int,] last = int)
6     int first() default 0;  // 聲明默認值,成為非必須提供項
7     int last();
8 }

  

2、定義委托處理類 BetweenSupplier:

 1 public class BetweenSupplier extends ParameterSupplier{
 2 
 3     @Override
 4     public List<PotentialAssignment> getValueSources(ParameterSignature sig) {
 5 
 6         // 自定義實參值列表
 7         List<PotentialAssignment> list = new ArrayList<PotentialAssignment>();
 8 
 9         // 獲取注解變量
10         Between between = sig.getAnnotation(Between.class);
11 
12         // 獲取通過注解@Between傳入的first值
13         int first = between.first();
14 
15         // 獲取通過注解@Between傳入的last值
16         int last = between.last();
17  
18         for (int i = first; i <= last; i++){
19           // PotentialAssignment.forValue(String name, Object value)
20           // name為value的描述標記,沒實際作用
21           // value為實參可選值
22             list.add(PotentialAssignment.forValue("name", i));
23         }
24         return list;
25     }
26 }

 

 3、調用方式:

1 @Theory
2 public final void test(@Between(last = 0) int i, @Between(first = 3, last= 10) int j) {
3     // i 取值為 0(first默認=0,last=0),j 取值為 3-10
4     assertTrue(i + j >= 0);
5 }

 

 

自定義實現II(靜態實參值表):

本方式以一個自定義注解接口@AllValue為例,展示如何靜態地內部創建固定的實參數據集。

 

1、定義annotation注解接口 AllValue(按“使用II”方式調用可省略此步驟):

1 @Retention(RetentionPolicy.RUNTIME)
2 // 聲明注解接口所使用的委托處理類
3 @ParametersSuppliedBy(AllValueSupplier.class)
4 // 空接口,不需要接受任何參數變量
5 public @interface AllValue{ }  

  

2、定義委托處理類 AllValueSupplier:

 1 public class AllValueSupplier extends ParameterSupplier{
 2 
 3     @Override
 4     public List<PotentialAssignment> getValueSources(ParameterSignature sig) {
 5         
 6         List<PotentialAssignment> list = new ArrayList<PotentialAssignment>();
 7   
 8       // 內定提供固定的可用實參值表
 9         for (int i = 0; i <= 100; i++){
10             list.add(PotentialAssignment.forValue("name", i));
11         }
12         return list;
13     }
14 }

  

3.1、使用I(使用自定義注解):

1 @Theory
2 public final void test(@AllValue int i) {
3     // i 取值為 0-100
4     assertTrue(i >= 0);
5 }

  

3.2、使用II(可省略第1步注解接口AllValue的定義):

1 @Theory
2 public final void test(@ParametersSuppliedBy(AllValueSupplier.class) int i) {
3     // i 取值為 0-100
4     assertTrue(i >= 0);
5 }

 

 

JUnit官方wiki及下載地址:https://github.com/junit-team/junit/wiki

Theory理論機制英文原文wiki(注:已過期,不適用於4.11):https://github.com/junit-team/junit/wiki/Theories


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM