在每個現代軟件包的構造階段,測試這一實踐都扮演着中心角色。過去那種先編寫代碼,然後有空的時候再測試(或者根本不測試)的日子已經一去不返,因爲大多 數開發人員現在認識到需要採用編碼和測試彼此交織、同步推進的軟件方法論,以便儘早發現 bug,在開發過程開始的時候就識別出主要的風險。
JUnit 超過了其他測試框架,推動開發人員理解了測試尤其是單元測試的用途。利用一個相當簡單、實用、嚴格的架構,JUnit 已經能夠“傳染”大量開發人員。(有關“被測試傳染”的更多信息,請參閱 參考資料。) JUnit 用戶已經學會了單元測試的一些基本規則:
- 每段代碼都必須經過測試。
- 只要有可能,代碼的測試必須隔離進行(例如,使用像 模擬對象 這樣的技術)。
- 軟件必須容易測試 —— 也就是說, 在編寫的時候要想着測試。
但是,隨着開發人員對測試的信任增長,JUnit 的簡單性和嚴格性把他們分成兩個相反的派別。一方面,有些人堅信 JUnit 的簡單性對於不斷地提醒程序員軟件也必須保持簡單來說是必不可少的(這稱爲 KISS 原則,代表 keep it simple, stupid);另一方面,有些人認爲 JUnit 不是簡單而是簡化,所以他們想要從測試框架得到新的高級特性、更大的靈活性和更強大的能力。JUnit 的一些特殊特性,就是爲了滿足這個羣體的一些具體批評而推出的:
- 因爲 Java 語言的單繼承性,所以必須擴展
TestCase
類的限制很大。 - 無法向 JUnit 的測試方法傳遞參數,也無法向
setUp()
和tearDown()
方法傳遞參數。 - 執行模型有點奇怪:每次執行一個測試方法的時候,都要重新實例化測試類。
- 管理複雜項目中的不同測試套件有可能非常複雜。
|
在本文中,您將學習到如何用這個叫做 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 的測試類是普通的老式 Java 對象;您不需要擴展任何特殊的類,也不需要使用測試方法的任何命名約定:您只要用標註
@Test
通知框架這個類的方法是測試。清單 1 演示了實用類
StringUtils
的一個最簡單的測試。它測試
StringUtils
的兩個方法:
isEmpty()
方法檢測
String
是否爲空;
trim()
方法從
String
兩端刪除控制字符。請注意,其中使用了 Java 指令
assert
來檢測錯誤情況。
清單 1. 針對類 StringUtils 的一個測試用例
|
但是,在運行測試之前,必須用特殊的 XML 文件配置 TestNG,習慣上把這個文件命名爲 testng.xml。這個文件的語法非常簡單,如清單 2 所示。這個文件首先定義測試套件
My test suite,這個套件只包含一個測試
First test,這個測試由
StringUtilsTest
類完成。
清單 2. TestNG 的配置文件
|
如果這個示例 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 構建文件
|
如 果一切正常,那麼應當在控制檯中看到測試結果。而且,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. 帶有不同組的配置文件
|
顯然,當運行不同的測試組時,HTML 報告能夠在單一列表中顯示所有測試,也可以在獨立的列表中顯示每個組的測試,從而能夠立即理解問題的來源。
|
使用 TestNG,不僅可以指定測試方法,還可以用專門的標註
@Configuration
指定類中的其他特定方法,這些方法叫做
配置方法。配置方法有四種類型:
-
beforeTestClass
方法在類實例化之後,但是在測試方法運行之前執行。 -
afterTestClass
方法在類中的所有測試方法執行之後執行。 -
beforeTestMethod
方法在類中的任何測試方法執行之前執行。 -
afterTestMethod
方法在類中的每個測試方法執行之後執行。
圖 2 進一步描述了測試類的生命週期。
圖 2. 測試類的生命週期
清單 5 演示了配置方法的一些示例。請注意,如果您使用組,那麼配置方法也必須屬於某個組。而且,配置方法的四種類型彼此之間不是互斥的,所以可以把方法定義成同時屬於一種或多種配置方法類型。(作爲例子,請參閱清單 5 中的
aroundTestMethods()
方法)。
清單 5. 配置方法示例
|
TestNG 中的配置方法是 JUnit 的
setUp()
和
tearDown()
方法的增強版;它們的主要目的是爲測試創建正確的執行上下文,並在測試用例執行之後刷新數據。
|
使用 TestNG,您可以非常簡單、非常容易地檢測異常的發生。很明顯,用 JUnit 也可以做這件事,但是正如您在清單 6 中的示例中所看到的,使用 TestNG 的
@ExpectedExceptions
標註可以使代碼編寫驚人地容易和簡單。
@ExpectedExceptions
標註指明框架能夠容忍拋出的
NumberFormatException
異常,所以不應當被當作是故障。要查看在某行代碼中是否拋出異常,您可以直接在這行代碼之後加入
assert false
語句。這意味着
只有 在指定行中拋出特定類型的異常的時候,您纔會通過測試。
清單 6. 用 TestNG 進行異常檢測
|
|
在本文中,我提供了 TestNG 的快速實用介紹,目的是展示如何開始編寫單元測試。但是,它不是一份完整的參考手冊。TestNG 還有許多其他非常有用的有趣特性:
- 可以向測試方法和配置方法傳遞參數,可以用標註或在 XML 配置文件中聲明參數。
- 可以在 TestNG 下用“兼容模式”運行過去運行良好的 Junit 測試。
- 可以在測試組之間建立依賴性,決定它們的執行順序。
要了解這個框架的所有潛力,有必要參閱 TestNG 的文檔(參閱 參考資料)。
所有這些特性,與用於定義測試的 Java 標註一起,使整個測試過程更加簡單、更加靈活。編寫測試必須遵守的規則 很少;除此之外,您絕對可以自由選擇自己喜歡的測試策略。
在使用 TestNG 時最明顯的是,這個模板已經是編寫單元測試的一個好選擇了,而且,在設計上,它與其他庫和工具的集成非常簡單,所以它未來的發展會給開發人員帶來一些有趣的新東西。