TestNG 使 Java 单元测试轻而易举
http://www.ibm.com/developerworks/cn/java/j-testng/
在每個現代軟件包的構造階段,測試這一實踐都扮演著中心角色。過去那種先編寫代碼,然后有空的時候再測試(或者根本不測試)的日子已經一去不返,因為大多數開發人員現在認識到需要采用編碼和測試彼此交織、同步推進的軟件方法論,以便盡早發現 bug,在開發過程開始的時候就識別出主要的風險。
JUnit 超過了其他測試框架,推動開發人員理解了測試尤其是單元測試的用途。利用一個相當簡單、實用、嚴格的架構,JUnit 已經能夠“傳染”大量開發人員。(有關“被測試傳染”的更多信息,請參閱 參考資料。) JUnit 用戶已經學會了單元測試的一些基本規則:
- 每段代碼都必須經過測試。
- 只要有可能,代碼的測試必須隔離進行(例如,使用像 模擬對象這樣的技術 )。
- 軟件必須容易測試 —— 也就是說, 在編寫的時候要想著測試。
但是,隨著開發人員對測試的信任增長,JUnit 的簡單性和嚴格性把他們分成兩個相反的派別。一方面,有些人堅信 JUnit 的簡單性對于不斷地提醒程序員軟件也必須保持簡單來說是必不可少的(這稱為 KISS 原則,代表 keep it simple, stupid);另一方面,有些人認為 JUnit 不是簡單而是簡化,所以他們想要從測試框架得到新的高級特性、更大的靈活性和更強大的能力。JUnit 的一些特殊特性,就是為了滿足這個群體的一些具體批評而推出的:
- 因為 Java 語言的單繼承性,所以必須擴展 TestCase類的限制很大。
- 無法向 JUnit 的測試方法傳遞參數,也無法向 setUp()和 tearDown()方法傳遞參數。
- 執行模型有點奇怪:每次執行一個測試方法的時候,都要重新實例化測試類。
- 管理復雜項目中的不同測試套件有可能非常復雜。
TestNG 的創造者
TestNG 的創造者是 Cedric Beust,他在 Java 編程領域非常出名,是 EJB 3 專家組的成員,也是其他一些流行的開源項目(例如 EJBGen 和 Doclipse)的創造者。TestNG 在 Apache 軟件許可條款約束下發布,并可從其 Web 站點下載(請參閱 參考資料中到該站點和 Cedric 站點的鏈接)。
在本文中,您將學習到如何用這個叫做 TestNG的新測試框架為應用程序編寫單元測試。TestNG 的靈感來自 JUnit,同時盡量保持后者的簡單性;但是,TestNG 消除了老框架的大多數限制,使開發人員可以編寫更加靈活、更加強大的測試。由于 TestNG 大量借用 Java Annotation(隨 JDK 5.0 引入;有關這個新特性的更多信息,請參閱 參考資料)來定義測試,所以本文也可以向您演示如何在實際的生產環境中使用 Java 語言的這個新特性。
關于代碼
為了演示 TestNG 的用法,我要為叫做 Jakarta Common Lang 的這個廣泛應用的開源庫(其中包含一些處理和操縱字符串、數字和 Java 對象的有用的類)編寫一些單元測試。在下面的 參考資料一節中,您可以找到 TestNG 和 Jakarta Common Lang 庫的鏈接;如果您想在自己的機器上隨著本文一起練習,這二者都需要下載。
可以在兩個不同的包中得到 TestNG:一個包要求 JDK 5.0,另一個包與 Java 語言 1.4 版本兼容。定義測試的時候,它們使用的語法略有差異:前者使用 JDK 5.0 標注,后者使用舊的 Javadoc 風格的標注。本文使用的是 JDK 5.0 版本,所以在繼續閱讀本文之前,需要對標注有基本的了解;您可以在 參考資料中找到關于這個主題的 developerWorks 資源的鏈接。但是,您要知道 只有在編譯和運行測試的時候才需要 JDK 5.0,所以您仍然可以用自己喜歡的編譯器來構建應用程序。實際上,您將用從 Jakarata 項目的 Web 站點下載的相同 JAR 文件來測試 Jakarta Common Lang 庫。關于使用 Java 平臺 1.4 版本的 TestNG 的更多細節,可以在 TestNG 的 Web 站點上找到。
最后,請單擊本文頂部或底部的 Code圖標,下載 j-testng-sample.zip 文件,其中包含一些示例,演示了如何用 TestNG 為 Jakarta Commons Lang 編寫單元測試。在里面,可以找到這里給出的大多數代碼,還有其他一些示例。閱讀本文并不需要這些代碼,但是它可以幫助您更加深入地理解在這里介紹的概念。
回頁首
TestNG 快速起步
TestNG 的測試類是普通的老式 Java 對象;您不需要擴展任何特殊的類,也不需要使用測試方法的任何命名約定:您只要用標注 @Test通知框架這個類的方法是測試。清單 1 演示了實用類 StringUtils的一個最簡單的測試。它測試 StringUtils的兩個方法: isEmpty()方法檢測 String是否為空; trim()方法從 String兩端刪除控制字符。請注意,其中使用了 Java 指令 assert來檢測錯誤情況。
清單 1. 針對類 StringUtils 的一個測試用例
| package tests; import com.beust.testng.annotations.*; import org.apache.commons.lang.StringUtils; public class StringUtilsTest { @Test public void isEmpty() { assert StringUtils.isBlank(null); assert StringUtils.isBlank(""); } @Test public void trim() { assert "foo".equals(StringUtils.trim(" foo ")); } } |
但是,在運行測試之前,必須用特殊的 XML 文件配置 TestNG,習慣上把這個文件命名為 testng.xml。這個文件的語法非常簡單,如清單 2 所示。這個文件首先定義測試套件 My test suite,這個套件只包含一個測試 First test,這個測試由 StringUtilsTest類完成。
清單 2. TestNG 的配置文件
| <!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" > <suite name="My test suite"> <test name="First test"> <classes> <class name="tests.StringUtilsTest" /> </classes> </test> </suite> |
如果這個示例 testng.xml 文件看起來沒什么用處(只有一個測試類),那么好消息是:這實際上是您定義測試套件時 惟一需要編寫的文件。還記得 JUnit 過去的日子么?在那些日子里,套件的定義可能分布在多個文件中:JUnit 的 TestSuite文件,屬性文件,還有當然缺不了的 Ant 構建文件。使用 TestNG,所有必需的數據都集中在 testng.xml 文件中。不需要額外的 TestSuite文件和構建文件。
要運行測試,請用 javac編譯類,然后用以下命令調用 TestNG :
| java -ea -classpath .;testng.jar;commons-lang-2.0.jar com.beust.testng.TestNG testng.xml |
在這里,選項 -ea告訴 JVM 處理斷言(在斷言失敗時拋出異常);運行這個例子只需要 testng.jar 和 commons-lang-2.0.jar 這兩個庫,而 com.beust.testng.TestNG是 TestNG 的主類。對于所有那些已經非常高興地忘記了 java和 javac的神秘語法的開發人員來說,還提供了一個有用的 Ant 任務。作為例子,清單 3 演示了本文發布的示例應用程序的 Ant 構建文件。請注意與類 com.beust.testng.TestNGAntTask關聯的 testng任務的定義,以及它在 test目標中相當簡單的用法。
清單 3. 帶有 TestNG 任務的 Ant 構建文件
| <project name="sample" default="test" basedir="."> <!-- COMPILE TESTS--> <path id="cpath"> <pathelement location="testng.jar"/> <pathelement location="commons-lang-2.0.jar"/> </path> <target name="compile"> <echo message="compiling tests"/> <mkdir dir="classes"/> <javac debug="true"source="1.5" classpathref="cpath"srcdir="src" destdir="classes"/> </target> <!-- RUN TESTS--> <taskdef name="testng"classname="com.beust.testng.TestNGAntTask"classpathref="cpath"/> <path id="runpath"> <path refid="cpath"/> <pathelement location="classes"/> </path> <target name="test" depends="compile"> <echo message="running tests"/> <testng fork="yes" classpathref="runpath" outputDir="test-output"> <fileset dir="src" includes="testng.xml"/> <jvmarg value="-ea" /> </testng> </target> </project> |
如果一切正常,那么應當在控制臺中看到測試結果。而且,TestNG 還在當前目錄下自動創建了一個叫做 test-output 的文件夾,并在其中創建了一份非常好的 HTML 報告。如果打開該報告并裝入 index.html,就可以看到與圖 1 中的頁面類似的頁面。
圖 1. TestNG 創建的 HTML 報告
回頁首
定義測試組
TestNG 另外一個有趣的特性是其定義測試組的能力。每個測試方法都可以與一個或多個組相關聯,但可以選擇只運行某個測試組。要把測試加入測試組,只要把組指定為 @Test標注的參數,使用的語法如下:
| @Test(groups = {"tests.string"}) |
在這個具體的例子中,您聲明:標注的方法屬于 tests.string組。因為參數 groups是一個數組,所以可以指定多個組,組名之間用逗號分隔。例如,在示例應用程序中,您可以為 String、Number 以及 boolean 創建不同的測試,然后如清單 4 所示配置 TestNG, 有選擇地運行它們 .
清單 4. 帶有不同組的配置文件
| <!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" > <suite name="My suite"> <test name="Simple example"> <groups> <run> <include name="tests.string" /> <include name="tests.math" /> <exclude name="tests.boolean"/> </run> </groups> <classes> .... list classes here.... </classes> </test> </suite> |
顯然,當運行不同的測試組時,HTML 報告能夠在單一列表中顯示所有測試,也可以在獨立的列表中顯示每個組的測試,從而能夠立即理解問題的來源。
回頁首
配置方法
使用 TestNG,不僅可以指定測試方法,還可以用專門的標注 @Configuration指定類中的其他特定方法,這些方法叫做 配置方法。配置方法有四種類型:
- beforeTestClass方法在類實例化之后,但是在測試方法運行之前執行。
- afterTestClass方法在類中的所有測試方法執行之后執行。
- beforeTestMethod方法在類中的任何測試方法執行之前執行。
- afterTestMethod方法在類中的每個測試方法執行之后執行。
圖 2 進一步描述了測試類的生命周期。
圖 2. 測試類的生命周期
清單 5 演示了配置方法的一些示例。請注意,如果您使用組,那么配置方法也必須屬于某個組。而且,配置方法的四種類型彼此之間不是互斥的,所以可以把方法定義成同時屬于一種或多種配置方法類型。(作為例子,請參閱清單 5 中的 aroundTestMethods()方法)。
清單 5. 配置方法示例
| @Configuration(beforeTestClass = true, groups = {"tests.workflow"}) public void setUp() { System.out.println("Initializing..."); } @Configuration(afterTestMethod = true, beforeTestMethod = true, groups = {"tests.workflow"}) public void aroundTestMethods() { System.out.println("Around Test"); } |
TestNG 中的配置方法是 JUnit 的 setUp()和 tearDown()方法的增強版;它們的主要目的是為測試創建正確的執行上下文,并在測試用例執行之后刷新數據。
回頁首
異常檢測
使用 TestNG,您可以非常簡單、非常容易地檢測異常的發生。很明顯,用 JUnit 也可以做這件事,但是正如您在清單 6 中的示例中所看到的,使用 TestNG 的 @ExpectedExceptions標注可以使代碼編寫驚人地容易和簡單。 @ExpectedExceptions標注指明框架能夠容忍拋出的 NumberFormatException異常,所以不應當被當作是故障。要查看在某行代碼中是否拋出異常,您可以直接在這行代碼之后加入 assert false語句。這意味著 只有在指定行中拋出特定類型的異常的時候,您才會通過測試。
清單 6. 用 TestNG 進行異常檢測
| public class NumberUtilsTest { @Test(groups = {"tests.math"}) @ExpectedExceptions(NumberFormatException.class) public void test() { NumberUtils.createDouble("12.23.45"); assert false; //shouldn't be invoked } } |
回頁首
結束語
在本文中,我提供了 TestNG 的快速實用介紹,目的是展示如何開始編寫單元測試。但是,它不是一份完整的參考手冊。TestNG 還有許多其他非常有用的有趣特性:
- 可以向測試方法和配置方法傳遞參數,可以用標注或在 XML 配置文件中聲明參數。
- 可以在 TestNG 下用“兼容模式”運行過去運行良好的 Junit 測試。
- 可以在測試組之間建立依賴性,決定它們的執行順序。
要了解這個框架的所有潛力,有必要參閱 TestNG 的文檔(參閱 參考資料)。
所有這些特性,與用于定義測試的 Java 標注一起,使整個測試過程更加簡單、更加靈活。編寫測試必須遵守的規則 很少;除此之外,您絕對可以自由選擇自己喜歡的測試策略。
在使用 TestNG 時最明顯的是,這個模板已經是編寫單元測試的一個好選擇了,而且,在設計上,它與其他庫和工具的集成非常簡單,所以它未來的發展會給開發人員帶來一些有趣的新東西。
回頁首
下載
| j-testng-sample.zip | ? | HTTP |
關于下載方法的信息
?
參考資料
- 您可以參閱本文在 developerWorks 全球站點上的 英文原文。
- 請單本文頂部或底部的 Code圖標下載 j-testng-sample.zip,其中包含本文中討論的代碼。
- 在項目的 Web 站點下載 TestNG。
- 運行本文的代碼,需要下載 Jakarta Commons Lang庫。
- 如果不熟悉 Java 標注,請閱讀 Brett McLaughlin 2004 年 9 月份開始在 developerWorks 上發表的由兩部分組成的系列文章:
- Tiger 中的注釋,第 1 部分 : 向 Java 代碼中添加元數據
- Tiger 中的注釋,第 2 部分 : 定制注釋
- 在 JUnit.org下載 JUnit。
- Malcolm Davis 撰寫的“ 利用 Ant 和 JUnit 進行增量開發”(developerWorks,2000 年 11 月)是一篇很好的文章,解釋了如何把 JUnit 集成到項目中。
- Eric Allen 和 Roy Miller 在他們各自的專欄 診斷 Java 代碼 和 極限編程 中都經常提及到單元測試。
- Testdriven.com是關于測試驅動開發的文章和資源的全面集合。
- 學習更多關于各種 開源測試工具。
- 學習更多關于“ 使用模仿對象進行單元測試”,Alexander Day Chaffee 和 William Pietri (developerWorks,2002 年 11 月)。
- 關于 JUnit、單元測試和 TestNG 的大量思想都記錄在 Cedric Beust's blog。
- 您是否 test infected?了解這個術語的意義。
- 請訪問 Developer Bookstore,獲得技術書籍的完整列表,其中包括數百本 Java 相關主題的圖書。
關于作者
Filippo Diotalevi 是位于米蘭的 IBM 意大利公司的 IT 專家,他主要是 J2EE 應用程序開發人員。他的興趣領域主要是模式、面向方面編程,以及敏捷方法論;他是 IBM 紅皮書 Pattern: Direct Connections for Intra- and Inter-enterprise 的合著者,以及發表在 Web 站點和雜志上的一些技術文章的作者,還是 Java User Group Milano 的創始人。可以通過 filippo.diotalevi@it.ibm.com 與 Filippo 聯系。
?
總結
以上是生活随笔為你收集整理的TestNG 使 Java 单元测试轻而易举的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: dwr反推技术
- 下一篇: Loadrunner 入门连载教程