輕松

          記述我學習java的里程

          常用鏈接

          統(tǒng)計

          積分與排名

          友情鏈接

          最新評論

          單元測試利器 JUnit 4

          本文主要介紹了如何使用 JUnit 4 提供的各種功能開展有效的單元測試,并通過一個實例演示了如何使用 Ant 執(zhí)行自動化的單元測試。本文假設(shè)讀者對 Eclipse 下進行 Java 開發(fā)有一定的經(jīng)驗,并了解 Java 5 中的注解(annotation)特性。

          引言

          毋庸置疑,程序員要對自己編寫的代碼負責,您不僅要保證它能通過編譯,正常地運行,而且要滿足需求和設(shè)計預期的效果。單元測試正是驗證代碼行為是否滿足預期的有效手段之一。但不可否認,做測試是件很枯燥無趣的事情,而一遍又一遍的測試則更是讓人生畏的工作。幸運的是,單元測試工具 JUnit 使這一切變得簡單藝術(shù)起來。

          JUnit 是 Java 社區(qū)中知名度最高的單元測試工具。它誕生于 1997 年,由 Erich Gamma 和 Kent Beck 共同開發(fā)完成。其中 Erich Gamma 是經(jīng)典著作《設(shè)計模式:可復用面向?qū)ο筌浖幕A(chǔ)》一書的作者之一,并在 Eclipse 中有很大的貢獻;Kent Beck 則是一位極限編程(XP)方面的專家和先驅(qū)。

          麻雀雖小,五臟俱全。JUnit 設(shè)計的非常小巧,但是功能卻非常強大。Martin Fowler 如此評價 JUnit:在軟件開發(fā)領(lǐng)域,從來就沒有如此少的代碼起到了如此重要的作用。它大大簡化了開發(fā)人員執(zhí)行單元測試的難度,特別是 JUnit 4 使用 Java 5 中的注解(annotation)使測試變得更加簡單。





          回頁首


          JUnit 4 初體驗

          在開始體驗 JUnit 4 之前,我們需要以下軟件的支持:

          • Eclipse:最為流行的 IDE,它全面集成了 JUnit,并從版本 3.2 開始支持 JUnit 4。當然 JUnit 并不依賴于任何 IDE。您可以從 http://www.eclipse.org/ 上下載最新的 Eclipse 版本。
          • Ant:基于 Java 的開源構(gòu)建工具,您可以在 http://ant.apache.org/ 上得到最新的版本和豐富的文檔。Eclipse 中已經(jīng)集成了 Ant,但是在撰寫本文時,Eclipse 使用的 Ant 版本較低(必需 1.7 或者以上版本),不能很好的支持 JUnit 4。
          • JUnit:它的官方網(wǎng)站是 http://www.junit.org/。您可以從上面獲取關(guān)于 JUnit 的最新消息。如果您和本文一樣在 Eclipse 中使用 JUnit,就不必再下載了。

          首先為我們的體驗新建一個 Java 工程 —— coolJUnit?,F(xiàn)在需要做的是,打開項目 coolJUnit 的屬性頁 -> 選擇“Java Build Path”子選項 -> 點選“Add Library…”按鈕 -> 在彈出的“Add Library”對話框中選擇 JUnit(圖1),并在下一頁中選擇版本 4.1 后點擊“Finish”按鈕。這樣便把 JUnit 引入到當前項目庫中了。


          圖1 為項目添加 JUnit 庫
          圖1 為項目添加 JUnit 庫
          請注意 JDK 的版本

          JUnit 4.1 是基于 Java 5 的升級版本,它使用了 Tiger 中的很多新特性來簡化原有的使用方式。正因為如此,它并不能直接運行在 JDK1.4.x 版本上。如果您需要在 JDK1.4.x 版本使用 JUnit 的話,請使用 3.8.1 版本。

          可以開始編寫單元測試了嗎?等等……,您打算把單元測試代碼放在什么地方呢?把它和被測試代碼混在一起,這顯然會照成混亂,因為單元測試代碼是不會出現(xiàn)在最終產(chǎn)品中的。建議您分別為單元測試代碼與被測試代碼創(chuàng)建單獨的目錄,并保證測試代碼和被測試代碼使用相同的包名。這樣既保證了代碼的分離,同時還保證了查找的方便。遵照這條原則,我們在項目 coolJUnit 根目錄下添加一個新目錄 testsrc,并把它加入到項目源代碼目錄中(加入方式見 圖2)。


          圖2 修改項目源代碼目錄
          圖2 修改項目源代碼目錄

          現(xiàn)在我們得到了一條 JUnit 的最佳實踐:單元測試代碼和被測試代碼使用一樣的包,不同的目錄。

          一切準備就緒,一起開始體驗如何使用 JUnit 進行單元測試吧。下面的例子來自筆者的開發(fā)實踐:工具類 WordDealUtil 中的靜態(tài)方法 wordFormat4DB 是專用于處理 Java 對象名稱向數(shù)據(jù)庫表名轉(zhuǎn)換的方法(您可以在代碼注釋中可以得到更多詳細的內(nèi)容)。下面是第一次編碼完成后大致情形:

          package com.ai92.cooljunit;
          
          import java.util.regex.Matcher;
          import java.util.regex.Pattern;
          
          /**
           * 對名稱、地址等字符串格式的內(nèi)容進行格式檢查
           * 或者格式化的工具類
           * 
           * @author Ai92
           */
          public class WordDealUtil {
          
          	/**
          	 * 將Java對象名稱(每個單詞的頭字母大寫)按照
          	 * 數(shù)據(jù)庫命名的習慣進行格式化
          	 * 格式化后的數(shù)據(jù)為小寫字母,并且使用下劃線分割命名單詞
          	 * 
          	 * 例如:employeeInfo 經(jīng)過格式化之后變?yōu)?employee_info
          	 * 
          	 * @param name	Java對象名稱
          	 */
          	public static String wordFormat4DB(String name){
          		Pattern p = Pattern.compile("[A-Z]");
          		Matcher m = p.matcher(name);
          		StringBuffer sb = new StringBuffer();
          		
          		while(m.find()){
          			m.appendReplacement(sb, "_"+m.group());
          		}
          		return m.appendTail(sb).toString().toLowerCase();
          	}
          }
          

          它是否能按照預期的效果執(zhí)行呢?嘗試為它編寫 JUnit 單元測試代碼如下:

          package com.ai92.cooljunit;
          
          import static org.junit.Assert.assertEquals;
          import org.junit.Test;
          
          public class TestWordDealUtil {
          	//測試wordFormat4DB正常運行的情況
          	@Test public void wordFormat4DBNormal(){
          		String target = "employeeInfo";
          		String result = WordDealUtil.wordFormat4DB(target);
          		
          		assertEquals("employee_info", result);
          	}
          }
          

          很普通的一個類嘛!測試類 TestWordDealUtil 之所以使用“Test”開頭,完全是為了更好的區(qū)分測試類與被測試類。測試方法 wordFormat4DBNormal 調(diào)用執(zhí)行被測試方法 WordDealUtil.wordFormat4DB,以判斷運行結(jié)果是否達到設(shè)計預期的效果。需要注意的是,測試方法 wordFormat4DBNormal 需要按照一定的規(guī)范書寫:

          1. 測試方法必須使用注解 org.junit.Test 修飾。
          2. 測試方法必須使用 public void 修飾,而且不能帶有任何參數(shù)。

          測試方法中要處理的字符串為“employeeInfo”,按照設(shè)計目的,處理后的結(jié)果應(yīng)該為“employee_info”。assertEquals 是由 JUnit 提供的一系列判斷測試結(jié)果是否正確的靜態(tài)斷言方法(位于類 org.junit.Assert 中)之一,我們使用它將執(zhí)行結(jié)果 result 和預期值“employee_info”進行比較,來判斷測試是否成功。

          看看運行結(jié)果如何。在測試類上點擊右鍵,在彈出菜單中選擇 Run As JUnit Test。運行結(jié)果如下圖所示:


          圖3 JUnit 運行成功界面
          圖3 JUnit 運行成功界面

          綠色的進度條提示我們,測試運行通過了。但現(xiàn)在就宣布代碼通過了單元測試還為時過早。記?。耗膯卧獪y試代碼不是用來證明您是對的,而是為了證明您沒有錯。因此單元測試的范圍要全面,比如對邊界值、正常值、錯誤值得測試;對代碼可能出現(xiàn)的問題要全面預測,而這也正是需求分析、詳細設(shè)計環(huán)節(jié)中要考慮的。顯然,我們的測試才剛剛開始,繼續(xù)補充一些對特殊情況的測試:

          public class TestWordDealUtil {
          	……
          	//測試 null 時的處理情況
          	@Test public void wordFormat4DBNull(){
          		String target = null;
          		String result = WordDealUtil.wordFormat4DB(target);
          		
          		assertNull(result);
          	}
          	
          	//測試空字符串的處理情況
          	@Test public void wordFormat4DBEmpty(){
          		String target = "";
          		String result = WordDealUtil.wordFormat4DB(target);
          		
          		assertEquals("", result);
          	}
          
          	//測試當首字母大寫時的情況
          	@Test public void wordFormat4DBegin(){
          		String target = "EmployeeInfo";
          		String result = WordDealUtil.wordFormat4DB(target);
          		
          		assertEquals("employee_info", result);
          	}
          	
          	//測試當尾字母為大寫時的情況
          	@Test public void wordFormat4DBEnd(){
          		String target = "employeeInfoA";
          		String result = WordDealUtil.wordFormat4DB(target);
          		
          		assertEquals("employee_info_a", result);
          	}
          	
          	//測試多個相連字母大寫時的情況
          	@Test public void wordFormat4DBTogether(){
          		String target = "employeeAInfo";
          		String result = WordDealUtil.wordFormat4DB(target);
          		
          		assertEquals("employee_a_info", result);
          	}
          }
          

          再次運行測試。很遺憾,JUnit 運行界面提示我們有兩個測試情況未通過測試(圖4)——當首字母大寫時得到的處理結(jié)果與預期的有偏差,造成測試失?。╢ailure);而當測試對 null 的處理結(jié)果時,則直接拋出了異常——測試錯誤(error)。顯然,被測試代碼中并沒有對首字母大寫和 null 這兩種特殊情況進行處理,修改如下:

          //修改后的方法wordFormat4DB
          /**
          	 * 將Java對象名稱(每個單詞的頭字母大寫)按照
          	 * 數(shù)據(jù)庫命名的習慣進行格式化
          	 * 格式化后的數(shù)據(jù)為小寫字母,并且使用下劃線分割命名單詞
          	 * 如果參數(shù)name為null,則返回null
          	 * 
          	 * 例如:employeeInfo 經(jīng)過格式化之后變?yōu)?employee_info
          	 * 
          	 * @param name Java對象名稱
          	 */
          	public static String wordFormat4DB(String name){
          		
          		if(name == null){
          			return null;
          		}
          		
          		Pattern p = Pattern.compile("[A-Z]");
          		Matcher m = p.matcher(name);
          		StringBuffer sb = new StringBuffer();
          		
          		while(m.find()){
          			if(m.start() != 0)
          				m.appendReplacement(sb, ("_"+m.group()).toLowerCase());
          		}
          		return m.appendTail(sb).toString().toLowerCase();
          	}
          


          圖4 JUnit 運行失敗界面
          圖4 JUnit 運行失敗界面

          JUnit 將測試失敗的情況分為兩種:failure 和 error。Failure 一般由單元測試使用的斷言方法判斷失敗引起,它表示在測試點發(fā)現(xiàn)了問題;而 error 則是由代碼異常引起,這是測試目的之外的發(fā)現(xiàn),它可能產(chǎn)生于測試代碼本身的錯誤(測試代碼也是代碼,同樣無法保證完全沒有缺陷),也可能是被測試代碼中的一個隱藏的bug。

          請牢記!

          請牢記這一條 JUnit 最佳實踐:測試任何可能的錯誤。單元測試不是用來證明您是對的,而是為了證明您沒有錯。

          啊哈,再次運行測試,綠條又重現(xiàn)眼前。通過對 WordDealUtil.wordFormat4DB 比較全面的單元測試,現(xiàn)在的代碼已經(jīng)比較穩(wěn)定,可以作為 API 的一部分提供給其它模塊使用了。

          不知不覺中我們已經(jīng)使用 JUnit 漂亮的完成了一次單元測試??梢泽w會到 JUnit 是多么輕量級,多么簡單,根本不需要花心思去研究,這就可以把更多的注意力放在更有意義的事情上——編寫完整全面的單元測試。





          回頁首


          JUnit 深入

          當然,JUnit 提供的功能決不僅僅如此簡單,在接下來的內(nèi)容中,我們會看到 JUnit 中很多有用的特性,掌握它們對您靈活的編寫單元測試代碼非常有幫助。

          Fixture

          何謂 Fixture?它是指在執(zhí)行一個或者多個測試方法時需要的一系列公共資源或者數(shù)據(jù),例如測試環(huán)境,測試數(shù)據(jù)等等。在編寫單元測試的過程中,您會發(fā)現(xiàn)在大部分的測試方法在進行真正的測試之前都需要做大量的鋪墊——為設(shè)計準備 Fixture 而忙碌。這些鋪墊過程占據(jù)的代碼往往比真正測試的代碼多得多,而且這個比率隨著測試的復雜程度的增加而遞增。當多個測試方法都需要做同樣的鋪墊時,重復代碼的“壞味道”便在測試代碼中彌漫開來。這股“壞味道”會弄臟您的代碼,還會因為疏忽造成錯誤,應(yīng)該使用一些手段來根除它。

          JUnit 專門提供了設(shè)置公共 Fixture 的方法,同一測試類中的所有測試方法都可以共用它來初始化 Fixture 和注銷 Fixture。和編寫 JUnit 測試方法一樣,公共 Fixture 的設(shè)置也很簡單,您只需要:

          1. 使用注解 org,junit.Before 修飾用于初始化 Fixture 的方法。
          2. 使用注解 org.junit.After 修飾用于注銷 Fixture 的方法。
          3. 保證這兩種方法都使用 public void 修飾,而且不能帶有任何參數(shù)。

          遵循上面的三條原則,編寫出的代碼大體是這個樣子:

          //初始化Fixture方法
          @Before public void init(){……}
          
          //注銷Fixture方法
          @After public void destroy(){……}
          

          這樣,在每一個測試方法執(zhí)行之前,JUnit 會保證 init 方法已經(jīng)提前初始化測試環(huán)境,而當此測試方法執(zhí)行完畢之后,JUnit 又會調(diào)用 destroy 方法注銷測試環(huán)境。注意是每一個測試方法的執(zhí)行都會觸發(fā)對公共 Fixture 的設(shè)置,也就是說使用注解 Before 或者 After 修飾的公共 Fixture 設(shè)置方法是方法級別的(圖5)。這樣便可以保證各個獨立的測試之間互不干擾,以免其它測試代碼修改測試環(huán)境或者測試數(shù)據(jù)影響到其它測試代碼的準確性。


          圖5 方法級別 Fixture 執(zhí)行示意圖
          圖5 方法級別 Fixture 執(zhí)行示意圖

          可是,這種 Fixture 設(shè)置方式還是引來了批評,因為它效率低下,特別是在設(shè)置 Fixture 非常耗時的情況下(例如設(shè)置數(shù)據(jù)庫鏈接)。而且對于不會發(fā)生變化的測試環(huán)境或者測試數(shù)據(jù)來說,是不會影響到測試方法的執(zhí)行結(jié)果的,也就沒有必要針對每一個測試方法重新設(shè)置一次 Fixture。因此在 JUnit 4 中引入了類級別的 Fixture 設(shè)置方法,編寫規(guī)范如下:

          1. 使用注解 org,junit.BeforeClass 修飾用于初始化 Fixture 的方法。
          2. 使用注解 org.junit.AfterClass 修飾用于注銷 Fixture 的方法。
          3. 保證這兩種方法都使用 public static void 修飾,而且不能帶有任何參數(shù)。

          類級別的 Fixture 僅會在測試類中所有測試方法執(zhí)行之前執(zhí)行初始化,并在全部測試方法測試完畢之后執(zhí)行注銷方法(圖6)。代碼范本如下:

          //類級別Fixture初始化方法
          @BeforeClass public static void dbInit(){……}
          	
          //類級別Fixture注銷方法
          	@AfterClass public static void dbClose(){……}
          


          圖6 類級別 Fixture 執(zhí)行示意圖
          圖6 類級別 Fixture 執(zhí)行示意圖

          異常以及時間測試

          注解 org.junit.Test 中有兩個非常有用的參數(shù):expected 和 timeout。參數(shù) expected 代表測試方法期望拋出指定的異常,如果運行測試并沒有拋出這個異常,則 JUnit 會認為這個測試沒有通過。這為驗證被測試方法在錯誤的情況下是否會拋出預定的異常提供了便利。舉例來說,方法 supportDBChecker 用于檢查用戶使用的數(shù)據(jù)庫版本是否在系統(tǒng)的支持的范圍之內(nèi),如果用戶使用了不被支持的數(shù)據(jù)庫版本,則會拋出運行時異常 UnsupportedDBVersionException。測試方法 supportDBChecker 在數(shù)據(jù)庫版本不支持時是否會拋出指定異常的單元測試方法大體如下:

          @Test(expected=UnsupportedDBVersionException.class)
          	public void unsupportedDBCheck(){
          		……
          }
          

          注解 org.junit.Test 的另一個參數(shù) timeout,指定被測試方法被允許運行的最長時間應(yīng)該是多少,如果測試方法運行時間超過了指定的毫秒數(shù),則JUnit認為測試失敗。這個參數(shù)對于性能測試有一定的幫助。例如,如果解析一份自定義的 XML 文檔花費了多于 1 秒的時間,就需要重新考慮 XML 結(jié)構(gòu)的設(shè)計,那單元測試方法可以這樣來寫:

          @Test(timeout=1000)
          	public void selfXMLReader(){
          		……
          }
          

          忽略測試方法

          JUnit 提供注解 org.junit.Ignore 用于暫時忽略某個測試方法,因為有時候由于測試環(huán)境受限,并不能保證每一個測試方法都能正確運行。例如下面的代碼便表示由于沒有了數(shù)據(jù)庫鏈接,提示 JUnit 忽略測試方法 unsupportedDBCheck:

          @ Ignore(“db is down”)
          @Test(expected=UnsupportedDBVersionException.class)
          	public void unsupportedDBCheck(){
          		……
          }
          

          但是一定要小心。注解 org.junit.Ignore 只能用于暫時的忽略測試,如果需要永遠忽略這些測試,一定要確認被測試代碼不再需要這些測試方法,以免忽略必要的測試點。

          測試運行器

          又一個新概念出現(xiàn)了——測試運行器,JUnit 中所有的測試方法都是由它負責執(zhí)行的。JUnit 為單元測試提供了默認的測試運行器,但 JUnit 并沒有限制您必須使用默認的運行器。相反,您不僅可以定制自己的運行器(所有的運行器都繼承自 org.junit.runner.Runner),而且還可以為每一個測試類指定使用某個具體的運行器。指定方法也很簡單,使用注解 org.junit.runner.RunWith 在測試類上顯式的聲明要使用的運行器即可:

          @RunWith(CustomTestRunner.class)
          public class TestWordDealUtil {
          ……
          }
          

          顯而易見,如果測試類沒有顯式的聲明使用哪一個測試運行器,JUnit 會啟動默認的測試運行器執(zhí)行測試類(比如上面提及的單元測試代碼)。一般情況下,默認測試運行器可以應(yīng)對絕大多數(shù)的單元測試要求;當使用 JUnit 提供的一些高級特性(例如即將介紹的兩個特性)或者針對特殊需求定制 JUnit 測試方式時,顯式的聲明測試運行器就必不可少了。

          測試套件

          在實際項目中,隨著項目進度的開展,單元測試類會越來越多,可是直到現(xiàn)在我們還只會一個一個的單獨運行測試類,這在實際項目實踐中肯定是不可行的。為了解決這個問題,JUnit 提供了一種批量運行測試類的方法,叫做測試套件。這樣,每次需要驗證系統(tǒng)功能正確性時,只執(zhí)行一個或幾個測試套件便可以了。測試套件的寫法非常簡單,您只需要遵循以下規(guī)則:

          1. 創(chuàng)建一個空類作為測試套件的入口。
          2. 使用注解 org.junit.runner.RunWith 和 org.junit.runners.Suite.SuiteClasses 修飾這個空類。
          3. 將 org.junit.runners.Suite 作為參數(shù)傳入注解 RunWith,以提示 JUnit 為此類使用套件運行器執(zhí)行。
          4. 將需要放入此測試套件的測試類組成數(shù)組作為注解 SuiteClasses 的參數(shù)。
          5. 保證這個空類使用 public 修飾,而且存在公開的不帶有任何參數(shù)的構(gòu)造函數(shù)。
          package com.ai92.cooljunit;
          
          import org.junit.runner.RunWith;
          import org.junit.runners.Suite;
          ……
          
          /**
           * 批量測試 工具包 中測試類
           * @author Ai92
           */
          @RunWith(Suite.class)
          @Suite.SuiteClasses({TestWordDealUtil.class})
          public class RunAllUtilTestsSuite {
          }
          

          上例代碼中,我們將前文提到的測試類 TestWordDealUtil 放入了測試套件 RunAllUtilTestsSuite 中,在 Eclipse 中運行測試套件,可以看到測試類 TestWordDealUtil 被調(diào)用執(zhí)行了。測試套件中不僅可以包含基本的測試類,而且可以包含其它的測試套件,這樣可以很方便的分層管理不同模塊的單元測試代碼。但是,您一定要保證測試套件之間沒有循環(huán)包含關(guān)系,否則無盡的循環(huán)就會出現(xiàn)在您的面前……。

          參數(shù)化測試

          回顧一下我們在小節(jié)“JUnit 初體驗”中舉的實例。為了保證單元測試的嚴謹性,我們模擬了不同類型的字符串來測試方法的處理能力,為此我們編寫大量的單元測試方法。可是這些測試方法都是大同小異:代碼結(jié)構(gòu)都是相同的,不同的僅僅是測試數(shù)據(jù)和期望值。有沒有更好的方法將測試方法中相同的代碼結(jié)構(gòu)提取出來,提高代碼的重用度,減少復制粘貼代碼的煩惱?在以前的 JUnit 版本上,并沒有好的解決方法,而現(xiàn)在您可以使用 JUnit 提供的參數(shù)化測試方式應(yīng)對這個問題。

          參數(shù)化測試的編寫稍微有點麻煩(當然這是相對于 JUnit 中其它特性而言):

          1. 為準備使用參數(shù)化測試的測試類指定特殊的運行器 org.junit.runners.Parameterized。
          2. 為測試類聲明幾個變量,分別用于存放期望值和測試所用數(shù)據(jù)。
          3. 為測試類聲明一個使用注解 org.junit.runners.Parameterized.Parameters 修飾的,返回值為 java.util.Collection 的公共靜態(tài)方法,并在此方法中初始化所有需要測試的參數(shù)對。
          4. 為測試類聲明一個帶有參數(shù)的公共構(gòu)造函數(shù),并在其中為第二個環(huán)節(jié)中聲明的幾個變量賦值。
          5. 編寫測試方法,使用定義的變量作為參數(shù)進行測試。

          我們按照這個標準,重新改造一番我們的單元測試代碼:

          package com.ai92.cooljunit;
          
          import static org.junit.Assert.assertEquals;
          
          import java.util.Arrays;
          import java.util.Collection;
          
          import org.junit.Test;
          import org.junit.runner.RunWith;
          import org.junit.runners.Parameterized;
          import org.junit.runners.Parameterized.Parameters;
          
          @RunWith(Parameterized.class)
          public class TestWordDealUtilWithParam {
          
          		private String expected;
          	
          		private String target;
          	
          		@Parameters
          		public static Collection words(){
          	    		return Arrays.asList(new Object[][]{
          	          	{"employee_info", "employeeInfo"},		//測試一般的處理情況
          	          	{null, null},							//測試 null 時的處理情況
          	          	{"", ""},								//測試空字符串時的處理情況
          	          	{"employee_info", "EmployeeInfo"},		//測試當首字母大寫時的情況
          	          	{"employee_info_a", "employeeInfoA"},	//測試當尾字母為大寫時的情況
          	          	{"employee_a_info", "employeeAInfo"}	//測試多個相連字母大寫時的情況
          	    		});
          		}
          	
          	 	/**
          	 	* 參數(shù)化測試必須的構(gòu)造函數(shù)
          	 	* @param expected	期望的測試結(jié)果,對應(yīng)參數(shù)集中的第一個參數(shù)
          	 	* @param target	測試數(shù)據(jù),對應(yīng)參數(shù)集中的第二個參數(shù)
          	 	*/
          		public TestWordDealUtilWithParam(String expected , String target){
          			this.expected = expected;
          			this.target = target;
          		}
          	
          	 	/**
          	 	* 測試將 Java 對象名稱到數(shù)據(jù)庫名稱的轉(zhuǎn)換
          	 	*/
          		@Test public void wordFormat4DB(){
          			assertEquals(expected, WordDealUtil.wordFormat4DB(target));
          		}
          }
          

          很明顯,代碼瘦身了。在靜態(tài)方法 words 中,我們使用二維數(shù)組來構(gòu)建測試所需要的參數(shù)列表,其中每個數(shù)組中的元素的放置順序并沒有什么要求,只要和構(gòu)造函數(shù)中的順序保持一致就可以了?,F(xiàn)在如果再增加一種測試情況,只需要在靜態(tài)方法 words 中添加相應(yīng)的數(shù)組即可,不再需要復制粘貼出一個新的方法出來了。





          回頁首


          JUnit 和 Ant

          隨著項目的進展,項目的規(guī)模在不斷的膨脹,為了保證項目的質(zhì)量,有計劃的執(zhí)行全面的單元測試是非常有必要的。但單靠JUnit提供的測試套件很難勝任這項工作,因為項目中單元測試類的個數(shù)在不停的增加,測試套件卻無法動態(tài)的識別新加入的單元測試類,需要手動修改測試套件,這是一個很容易遺忘得步驟,稍有疏忽就會影響全面單元測試的覆蓋率。

          當然解決的方法有多種多樣,其中將 JUnit 與構(gòu)建利器 Ant 結(jié)合使用可以很簡單的解決這個問題。Ant —— 備受贊譽的 Java 構(gòu)建工具。它憑借出色的易用性、平臺無關(guān)性以及對項目自動測試和自動部署的支持,成為眾多項目構(gòu)建過程中不可或缺的獨立工具,并已經(jīng)成為事實上的標準。Ant 內(nèi)置了對 JUnit 的支持,它提供了兩個 Task:junit 和 junitreport,分別用于執(zhí)行 JUnit 單元測試和生成測試結(jié)果報告。使用這兩個 Task 編寫構(gòu)建腳本,可以很簡單的完成每次全面單元測試的任務(wù)。

          不過,在使用 Ant 運行 JUnit 之前,您需要稍作一些配置。打開 Eclipse 首選項界面,選擇 Ant -> Runtime 首選項(見圖7),將 JUnit 4.1 的 JAR 文件添加到 Classpath Tab 頁中的 Global Entries 設(shè)置項里。記得檢查一下 Ant Home Entries 設(shè)置項中的 Ant 版本是否在 1.7.0 之上,如果不是請?zhí)鎿Q為最新版本的 Ant JAR 文件。


          圖7 Ant Runtime 首選項
          圖7 Ant Runtime 首選項

          剩下的工作就是要編寫 Ant 構(gòu)建腳本 build.xml。雖然這個過程稍嫌繁瑣,但這是一件一勞永逸的事情?,F(xiàn)在我們就把前面編寫的測試用例都放置到 Ant 構(gòu)建腳本中執(zhí)行,為項目 coolJUnit 的構(gòu)建腳本添加一下內(nèi)容:

          <?xml version="1.0"?>
          <!-- ============================================= 
               auto unittest task    
               ai92                                                                
               ========================================== -->
          <project name="auto unittest task" default="junit and report" basedir=".">
          
          		<property name="output folder" value="bin"/>
          
          		<property name="src folder" value="src"/>
          	
          		<property name="test folder" value="testsrc"/>
          	
          		<property name="report folder" value="report" />
          
          		<!-- - - - - - - - - - - - - - - - - - 
                    target: test report folder init                      
                   - - - - - - - - - - - - - - - - - -->
          		<target name="test init">
          			<mkdir dir="${report folder}"/>
          		</target>
          	
          		<!-- - - - - - - - - - - - - - - - - - 
                    target: compile                      
                   - - - - - - - - - - - - - - - - - -->
          		<target name="compile">
          			<javac srcdir="${src folder}" destdir="${output folder}" />
          			<echo>compilation complete!</echo>
          		</target>
          
          		<!-- - - - - - - - - - - - - - - - - - 
                    target: compile test cases                      
                   - - - - - - - - - - - - - - - - - -->
          		<target name="test compile" depends="test init">
          			<javac srcdir="${test folder}" destdir="${output folder}" />
          			<echo>test compilation complete!</echo>
          		</target>
          	
          		<target name="all compile" depends="compile, test compile">
          		</target>
          	
          		<!-- ======================================== 
                    target: auto test all test case and output report file                      
                	===================================== -->
          		<target name="junit and report" depends="all compile">
          			<junit printsummary="on" fork="true" showoutput="true">
          				<classpath>
          					<fileset dir="lib" includes="**/*.jar"/>
          					<pathelement path="${output folder}"/>
          				</classpath>
          				<formatter type="xml" />
          				<batchtest todir="${report folder}">
          					<fileset dir="${output folder}">
          						<include name="**/Test*.*" />
          					</fileset>
          				</batchtest>
          			</junit>
          			<junitreport todir="${report folder}">
          				<fileset dir="${report folder}">
          					<include name="TEST-*.xml" />
          				</fileset>
          				<report format="frames" todir="${report folder}" />
          			</junitreport>
          		</target>
          </project>
          

          Target junit report 是 Ant 構(gòu)建腳本中的核心內(nèi)容,其它 target 都是為它的執(zhí)行提供前期服務(wù)。Task junit 會尋找輸出目錄下所有命名以“Test”開頭的 class 文件,并執(zhí)行它們。緊接著 Task junitreport 會將執(zhí)行結(jié)果生成 HTML 格式的測試報告(圖8)放置在“report folder”下。

          為整個項目的單元測試類確定一種命名風格。不僅是出于區(qū)分類別的考慮,這為 Ant 批量執(zhí)行單元測試也非常有幫助,比如前面例子中的測試類都已“Test”打頭,而測試套件則以“Suite”結(jié)尾等等。


          圖8 junitreport 生成的測試報告
          圖8 junitreport 生成的測試報告

          現(xiàn)在執(zhí)行一次全面的單元測試變得非常簡單了,只需要運行一下 Ant 構(gòu)建腳本,就可以走完所有流程,并能得到一份詳盡的測試報告。您可以在 Ant 在線手冊 中獲得上面提及的每一個 Ant 內(nèi)置 task 的使用細節(jié)。





          回頁首


          總結(jié)

          隨著越來越多的開發(fā)人員開始認同并接受極限編程(XP)的思想,單元測試的作用在軟件工程中變得越來越重要。本文旨在將最新的單元測試工具 JUnit 4 介紹給您,以及如何結(jié)合 IDE Eclipse 和構(gòu)建工具 Ant 創(chuàng)建自動化單元測試方案。并且還期望您能夠通過本文“感染”一些好的單元測試意識,因為 JUnit 本身僅僅是一份工具而已,它的真正優(yōu)勢來自于它的思想和技術(shù)。






          回頁首


          下載

          描述 名字 大小 下載方法
          本文示例代碼 coolJUnit.zip 24 KB HTTP
          關(guān)于下載方法的信息


          參考資料

          學習

          獲得產(chǎn)品和技術(shù)

          posted on 2007-03-05 16:15 輕松 閱讀(2094) 評論(1)  編輯  收藏 所屬分類: Junit相關(guān)JAVA轉(zhuǎn)貼

          評論

          # re: 單元測試利器 JUnit 4 2011-07-26 10:02 Lorella

          頂~看了那么多 就這個最詳細!~謝謝樓主  回復  更多評論   

          主站蜘蛛池模板: 庄河市| 华容县| 余干县| 凤冈县| 抚松县| 满洲里市| 永州市| 水城县| 舒兰市| 台前县| 聂拉木县| 扬中市| 泽库县| 疏勒县| 于田县| 乐山市| 温泉县| 合川市| 阆中市| 阳春市| 南京市| 凤台县| 滦平县| 安西县| 高州市| 泽州县| 息烽县| 闸北区| 彝良县| 彭阳县| 大连市| 永修县| 山阳县| 浦东新区| 灵宝市| 乐昌市| 井冈山市| 珠海市| 徐闻县| 卢氏县| 石狮市|