TestNG使Java單元測(cè)試輕而易舉
JUnit 框架是 Java 語(yǔ)言單元測(cè)試當(dāng)前的一站式解決方案。這個(gè)框架值得稱贊,因?yàn)樗褱y(cè)試驅(qū)動(dòng)的開(kāi)發(fā)思想介紹給 Java 開(kāi)發(fā)人員并教給他們
如何有效地編寫(xiě)單元測(cè)試。但是,在過(guò)去的幾年中,JUnit 的改進(jìn)不大;所以,為當(dāng)今復(fù)雜的環(huán)境編寫(xiě)測(cè)試已經(jīng)變成一個(gè)越來(lái)越困難的任務(wù),即
JUnit 必須與其他一些補(bǔ)充性測(cè)試框架集成起來(lái)。在本文中,F(xiàn)ilippo Diotalevi 介紹了 TestNG,這是一個(gè)測(cè)試 Java
應(yīng)用程序的新框架。TestNG 不僅確實(shí)強(qiáng)大、創(chuàng)新、可擴(kuò)展、靈活,它還展示了 Java Annotations(JDK 5.0 中的重大新特性)
的有趣應(yīng)用。
在每個(gè)現(xiàn)代軟件包的構(gòu)造階段,測(cè)試這一實(shí)踐都扮演著中心角色。過(guò)去那種先編寫(xiě)代碼,然后有空的時(shí)候再測(cè)試(或者根本不測(cè)試) 的日子已經(jīng)一去不返,因?yàn)榇蠖鄶?shù)開(kāi)發(fā)人員現(xiàn)在認(rèn)識(shí)到需要采用編碼和測(cè)試彼此交織、同步推進(jìn)的軟件方法論,以便盡早發(fā)現(xiàn) bug,在開(kāi)發(fā)過(guò)程開(kāi)始的時(shí)候就識(shí) 別出主要的風(fēng)險(xiǎn)。
JUnit 超過(guò)了其他測(cè)試框架,推動(dòng)開(kāi)發(fā)人員理解了測(cè)試尤其是單元測(cè)試的用途。利用一個(gè)相當(dāng)簡(jiǎn)單、實(shí)用、嚴(yán)格的架構(gòu), JUnit 已經(jīng)能夠“傳染”大量開(kāi)發(fā)人員。(有關(guān)“被測(cè)試傳染”的更多信息,請(qǐng)參閱 參考資料。) JUnit 用戶已經(jīng)學(xué)會(huì)了單元測(cè)試的一些基本規(guī) 則:
每段代碼都必須經(jīng)過(guò)測(cè)試。
只要有可能,代碼的測(cè)試必須隔離進(jìn)行(例如,使用像 模擬對(duì)象 這樣的技術(shù))。
軟件必須容易測(cè)試 ?? 也就是說(shuō),在編寫(xiě)的時(shí)候要想著測(cè)試。
但 是,隨著開(kāi)發(fā)人員對(duì)測(cè)試的信任增長(zhǎng),JUnit 的簡(jiǎn)單性和嚴(yán)格性把他們分成兩個(gè)相反的派別。一方面,有些人堅(jiān)信 JUnit 的簡(jiǎn)單性對(duì)于不斷地提醒程 序員軟件也必須保持簡(jiǎn)單來(lái)說(shuō)是必不可少的(這稱為 KISS 原則,代表 keep it simple, stupid);另一方面,有些人認(rèn)為 JUnit 不是簡(jiǎn)單而是簡(jiǎn)化,所以他們想要從測(cè)試框架得到新的高級(jí)特性、更大的靈活性和更強(qiáng)大的能力。JUnit 的一些特殊特性,就是為了滿足這個(gè) 群體的一些具體批評(píng)而推出的:
因?yàn)?nbsp;Java 語(yǔ)言的單繼承性,所以必須擴(kuò)展 TestCase 類的限制很大。
無(wú)法向 JUnit 的測(cè)試方法傳遞參數(shù),也無(wú)法向 setUp() 和 tearDown() 方法傳遞參數(shù)。
執(zhí)行模型有點(diǎn)奇怪:每次執(zhí)行一個(gè)測(cè)試方法的時(shí)候,都要重新實(shí)例化測(cè)試類。
管理復(fù)雜項(xiàng)目中的不同測(cè)試套件有可能非常復(fù)雜。
TestNG 的創(chuàng)造者
TestNG 的創(chuàng)造者是 Cedric Beust,他在 Java 編程領(lǐng)域非常出名,是 EJB 3 專家組的成員,也是其他一些流行的開(kāi)源項(xiàng)目(例如 EJBGen 和 Doclipse)的創(chuàng)造者。TestNG 在 Apache 軟件許可條款約束下發(fā)布,并可從其 Web 站點(diǎn)下載(請(qǐng)參閱 參考 資料 中到該站點(diǎn)和 Cedric 站點(diǎn)的鏈接)。
在本文中,您將學(xué)習(xí)到如何用這個(gè)叫做 TestNG 的新測(cè)試框架 為應(yīng)用程序編寫(xiě)單元測(cè)試。TestNG 的靈感來(lái)自 JUnit,同時(shí)盡量保持后者的簡(jiǎn)單性;但是,TestNG 消除了老框架的大多數(shù)限制,使開(kāi)發(fā)人員 可以編寫(xiě)更加靈活、更加強(qiáng)大的測(cè)試。由于 TestNG 大量借用 Java Annotation(隨 JDK 5.0 引入;有關(guān)這個(gè)新特性的更多信 息,請(qǐng)參閱 參考資料)來(lái)定義測(cè)試,所以本文也可以向您演示如何在實(shí)際的生產(chǎn)環(huán)境中使用 Java 語(yǔ)言的這個(gè)新特性。
關(guān)于代碼
為 了演示 TestNG 的用法,我要為叫做 Jakarta Common Lang 的這個(gè)廣泛應(yīng)用的開(kāi)源庫(kù)(其中包含一些處理和操縱字符串、數(shù)字和 Java 對(duì)象的有用的類)編寫(xiě)一些單元測(cè)試。在下面的 參考資料 一節(jié)中,您可以找到 TestNG 和 Jakarta Common Lang 庫(kù)的鏈接;如果您想在自己的機(jī)器上隨著本文一起練習(xí),這二者都需要下載。
可以在兩個(gè)不同的包中得到 TestNG:一個(gè)包要求 JDK 5.0,另一個(gè)包與 Java 語(yǔ)言 1.4 版本兼容。定義測(cè)試的時(shí)候,它們使用的語(yǔ)法略有差異:前者使用 JDK 5.0 標(biāo)注,后者使用 舊的 Javadoc 風(fēng)格的標(biāo)注。本文使用的是 JDK 5.0 版本,所以在繼續(xù)閱讀本文之前,需要對(duì)標(biāo)注有基本的了解;您可以在 參考資料 中找到 關(guān)于這個(gè)主題的 developerWorks 資源的鏈接。但是,您要知道 只有在編譯和運(yùn)行測(cè)試的時(shí)候 才需要 JDK 5.0,所以您仍然可以用自 己喜歡的編譯器來(lái)構(gòu)建應(yīng)用程序。實(shí)際上,您將用從 Jakarata 項(xiàng)目的 Web 站點(diǎn)下載的相同 JAR 文件來(lái)測(cè)試 Jakarta Common Lang 庫(kù)。關(guān)于使用 Java 平臺(tái) 1.4 版本的 TestNG 的更多細(xì)節(jié),可以在 TestNG 的 Web 站點(diǎn)上找到。
最后,請(qǐng)單擊本文頂部或底部的 Code 圖標(biāo),下載 j-testng-sample.zip 文件,其中包 含一些示例,演示了如何用 TestNG 為 Jakarta Commons Lang 編寫(xiě)單元測(cè)試。在里面,可以找到這里給出的大多數(shù)代碼,還有其 他一些示例。閱讀本文并不需要這些代碼,但是它可以幫助您更加深入地理解在這里介紹的概念。
TestNG 快速起步
TestNG 的測(cè)試類是普通的老式 Java 對(duì)象;您不需要擴(kuò)展任何特殊的類,也不需要使用測(cè)試方法的任何命名約定:您只要用標(biāo)注 @Test 通知框架這個(gè)類的方 法是測(cè)試。清單 1 演示了實(shí)用類 StringUtils 的一個(gè)最簡(jiǎn)單的測(cè)試。它測(cè)試 StringUtils 的兩個(gè)方法: isEmpty() 方法檢測(cè) String 是否為空; trim() 方法從 String 兩端刪除控制字符。請(qǐng)注意,其中使用了 Java 指令 assert 來(lái)檢 測(cè)錯(cuò)誤情況。
清單 1. 針對(duì)類 StringUtils 的一個(gè)測(cè)試用例
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 "));
}
}
但 是,在運(yùn)行測(cè)試之前,必須用特殊的 XML 文件配置 TestNG,習(xí)慣上把這個(gè)文件命名為 testng.xml。這個(gè)文件的語(yǔ)法非常簡(jiǎn)單,如清單 2 所示。這個(gè)文件首先定義測(cè)試套件 My test suite,這個(gè)套件只包含一個(gè)測(cè)試 First test,這個(gè)測(cè)試由 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>
如 果這個(gè)示例 testng.xml 文件看起來(lái)沒(méi)什么用處(只有一個(gè)測(cè)試類),那么好消息是:這實(shí)際上是您定義測(cè)試套件時(shí) 惟一需要編寫(xiě)的文件。還記得 JUnit 過(guò)去的日子么?在那些日子里,套件的定義可能分布在多個(gè)文件中:JUnit 的 TestSuite 文件,屬性文件,還有當(dāng)然缺不了的 Ant 構(gòu)建文件。使用 TestNG,所有必需的數(shù)據(jù)都集中在 testng.xml 文件中。不需要額外的 TestSuite 文件和構(gòu)建文件。
要運(yùn)行測(cè)試,請(qǐng)用 javac 編譯類,然后用以下命令調(diào)用 TestNG :
java -ea -classpath .;testng.jar;commons-lang-2.0.jar com.beust.testng.TestNG testng.xml
在 這里,選項(xiàng) -ea 告訴 JVM 處理斷言(在斷言失敗時(shí)拋出異常);運(yùn)行這個(gè)例子只需要 testng.jar 和 commons-lang- 2.0.jar 這兩個(gè)庫(kù),而 com.beust.testng.TestNG 是 TestNG 的主類。對(duì)于所有那些已經(jīng)非常高興地忘記了 java 和 javac 的神秘語(yǔ)法的開(kāi)發(fā)人員來(lái)說(shuō),還提供了一個(gè)有用的 Ant 任務(wù)。作為例子,清單 3 演示了本文發(fā)布的示例應(yīng)用程序的 Ant 構(gòu)建文件。請(qǐng)注意與類 com.beust.testng.TestNGAntTask 關(guān)聯(lián)的 testng 任務(wù)的定義,以及它在 test 目標(biāo)中相當(dāng)簡(jiǎn)單的用法。
清單 3. 帶有 TestNG 任務(wù)的 Ant 構(gòu)建文件
<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>
如 果一切正常,那么應(yīng)當(dāng)在控制臺(tái)中看到測(cè)試結(jié)果。而且,TestNG 還在當(dāng)前目錄下自動(dòng)創(chuàng)建了一個(gè)叫做 test-output 的文件夾,并在其中創(chuàng)建 了一份非常好的 HTML 報(bào)告。如果打開(kāi)該報(bào)告并裝入 index.html,就可以看到與圖 1 中的頁(yè)面類似的頁(yè)面。
圖 1. TestNG 創(chuàng)建的 HTML 報(bào)告
定義測(cè)試組
TestNG 另外一個(gè)有趣的特性是其定義測(cè)試組的能力。每個(gè)測(cè)試方法都可以與一個(gè)或多個(gè)組相關(guān)聯(lián),但可以選擇只運(yùn)行某個(gè)測(cè)試組。要把測(cè)試加入測(cè)試組,只要把組指定為 @Test 標(biāo)注的參數(shù),使用的語(yǔ)法如下:
@Test(groups = {"tests.string"})
在 這個(gè)具體的例子中,您聲明:標(biāo)注的方法屬于 tests.string 組。因?yàn)閰?shù) groups 是一個(gè)數(shù)組,所以可以指定多個(gè)組,組名之間用逗號(hào)分 隔。例如,在示例應(yīng)用程序中,您可以為 String、Number 以及 boolean 創(chuàng)建不同的測(cè)試,然后如清單 4 所示配置 TestNG, 有選擇地運(yùn)行它們.
清單 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>
顯然,當(dāng)運(yùn)行不同的測(cè)試組時(shí),HTML 報(bào)告能夠在單一列表中顯示所有測(cè)試,也可以在獨(dú)立的列表中顯示每個(gè)組的測(cè)試,從而能夠立即理解問(wèn)題的來(lái)源。
配置方法
使用 TestNG,不僅可以指定測(cè)試方法,還可以用專門(mén)的標(biāo)注 @Configuration 指定類中的其他特定方法,這些方法叫做 配置方法。配置方法有四種類型:
beforeTestClass 方法在類實(shí)例化之后,但是在測(cè)試方法運(yùn)行之前執(zhí)行。
afterTestClass 方法在類中的所有測(cè)試方法執(zhí)行之后執(zhí)行。
beforeTestMethod 方法在類中的任何測(cè)試方法執(zhí)行之前執(zhí)行。
afterTestMethod 方法在類中的每個(gè)測(cè)試方法執(zhí)行之后執(zhí)行。
圖 2 進(jìn)一步描述了測(cè)試類的生命周期。
圖 2. 測(cè)試類的生命周期
清 單 5 演示了配置方法的一些示例。請(qǐng)注意,如果您使用組,那么配置方法也必須屬于某個(gè)組。而且,配置方法的四種類型彼此之間不是互斥的,所以可以把方法 定義成同時(shí)屬于一種或多種配置方法類型。(作為例子,請(qǐng)參閱清單 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() 方法的增強(qiáng)版;它們的主要目的是為測(cè)試創(chuàng)建正確的執(zhí)行上下文,并在測(cè)試用例執(zhí)行之后刷新數(shù)據(jù)。
異常檢測(cè)
使 用 TestNG,您可以非常簡(jiǎn)單、非常容易地檢測(cè)異常的發(fā)生。很明顯,用 JUnit 也可以做這件事,但是正如您在清單 6 中的示例中所看到的,使 用 TestNG 的 @ExpectedExceptions 標(biāo)注可以使代碼編寫(xiě)驚人地容易和簡(jiǎn)單。@ExpectedExceptions 標(biāo)注指 明框架能夠容忍拋出的 NumberFormatException 異常,所以不應(yīng)當(dāng)被當(dāng)作是故障。要查看在某行代碼中是否拋出異常,您可以直接在這行 代碼之后加入 assert false 語(yǔ)句。這意味著 只有 在指定行中拋出特定類型的異常的時(shí)候,您才會(huì)通過(guò)測(cè)試。
清單 6. 用 TestNG 進(jìn)行異常檢測(cè)
public class NumberUtilsTest
{
@Test(groups = {"tests.math"})
@ExpectedExceptions(NumberFormatException.class)
public void test()
{
NumberUtils.createDouble("12.23.45");
assert false; //shouldn't be invoked
}
}
結(jié)束語(yǔ)
在本文中,我提供了 TestNG 的快速實(shí)用介紹,目的是展示如何開(kāi)始編寫(xiě)單元測(cè)試。但是,它不是一份完整的參考手冊(cè)。TestNG 還有許多其他非常有用的有趣特性:
可以向測(cè)試方法和配置方法傳遞參數(shù),可以用標(biāo)注或在 XML 配置文件中聲明參數(shù)。
可以在 TestNG 下用“兼容模式”運(yùn)行過(guò)去運(yùn)行良好的 Junit 測(cè)試。
可以在測(cè)試組之間建立依賴性,決定它們的執(zhí)行順序。
要了解這個(gè)框架的所有潛力,有必要參閱 TestNG 的文檔(參閱 參考資料)。
所有這些特性,與用于定義測(cè)試的 Java 標(biāo)注一起,使整個(gè)測(cè)試過(guò)程更加簡(jiǎn)單、更加靈活。編寫(xiě)測(cè)試必須遵守的規(guī)則 很少;除此之外,您絕對(duì)可以自由選擇自己喜歡的測(cè)試策略。
在使用 TestNG 時(shí)最明顯的是,這個(gè)模板已經(jīng)是編寫(xiě)單元測(cè)試的一個(gè)好選擇了,而且,在設(shè)計(jì)上,它與其他庫(kù)和工具的集成非常簡(jiǎn)單,所以它未來(lái)的發(fā)展會(huì)給開(kāi)發(fā)人員帶來(lái)一些有趣的新東西。
參考資料
您可以參閱本文在 developerWorks 全球站點(diǎn)上的 英文原文。
請(qǐng)單本文頂部或底部的 Code 圖標(biāo)下載 j-testng-sample.zip,其中包含本文中討論的代碼。
在項(xiàng)目的 Web 站點(diǎn) 下載 TestNG。
運(yùn)行本文的代碼,需要下載 Jakarta Commons Lang 庫(kù)。
如果不熟悉 Java 標(biāo)注,請(qǐng)閱讀 Brett McLaughlin 2004 年 9 月份開(kāi)始在 developerWorks 上發(fā)表的由兩部分組成的系列文章:
Tiger 中的注釋,第 1 部分: 向 Java 代碼中添加元數(shù)據(jù)
Tiger 中的注釋,第 2 部分: 定制注釋
在 JUnit.org 下載 JUnit。
Malcolm Davis 撰寫(xiě)的“利用 Ant 和 JUnit 進(jìn)行增量開(kāi)發(fā)”(developerWorks,2000 年 11 月)是一篇很好的文章,解釋了如何把 JUnit 集成到項(xiàng)目中。
Eric Allen 和 Roy Miller 在他們各自的專欄 診斷 Java 代碼 和 極限編程 中都經(jīng)常提及到單元測(cè)試。
Testdriven.com 是關(guān)于測(cè)試驅(qū)動(dòng)開(kāi)發(fā)的文章和資源的全面集合。
學(xué)習(xí)更多關(guān)于各種 開(kāi)源測(cè)試工具。
學(xué)習(xí)更多關(guān)于“使用模仿對(duì)象進(jìn)行單元測(cè)試”,Alexander Day Chaffee 和 William Pietri (developerWorks,2002 年 11 月)。
關(guān)于 JUnit、單元測(cè)試和 TestNG 的大量思想都記錄在 Cedric Beust's blog。
您是否 test infected?了解這個(gè)術(shù)語(yǔ)的意義。
請(qǐng)?jiān)L問(wèn) Developer Bookstore,獲得技術(shù)書(shū)籍的完整列表,其中包括數(shù)百本 Java 相關(guān)主題 的圖書(shū)。
在每個(gè)現(xiàn)代軟件包的構(gòu)造階段,測(cè)試這一實(shí)踐都扮演著中心角色。過(guò)去那種先編寫(xiě)代碼,然后有空的時(shí)候再測(cè)試(或者根本不測(cè)試) 的日子已經(jīng)一去不返,因?yàn)榇蠖鄶?shù)開(kāi)發(fā)人員現(xiàn)在認(rèn)識(shí)到需要采用編碼和測(cè)試彼此交織、同步推進(jìn)的軟件方法論,以便盡早發(fā)現(xiàn) bug,在開(kāi)發(fā)過(guò)程開(kāi)始的時(shí)候就識(shí) 別出主要的風(fēng)險(xiǎn)。
JUnit 超過(guò)了其他測(cè)試框架,推動(dòng)開(kāi)發(fā)人員理解了測(cè)試尤其是單元測(cè)試的用途。利用一個(gè)相當(dāng)簡(jiǎn)單、實(shí)用、嚴(yán)格的架構(gòu), JUnit 已經(jīng)能夠“傳染”大量開(kāi)發(fā)人員。(有關(guān)“被測(cè)試傳染”的更多信息,請(qǐng)參閱 參考資料。) JUnit 用戶已經(jīng)學(xué)會(huì)了單元測(cè)試的一些基本規(guī) 則:
每段代碼都必須經(jīng)過(guò)測(cè)試。
只要有可能,代碼的測(cè)試必須隔離進(jìn)行(例如,使用像 模擬對(duì)象 這樣的技術(shù))。
軟件必須容易測(cè)試 ?? 也就是說(shuō),在編寫(xiě)的時(shí)候要想著測(cè)試。
但 是,隨著開(kāi)發(fā)人員對(duì)測(cè)試的信任增長(zhǎng),JUnit 的簡(jiǎn)單性和嚴(yán)格性把他們分成兩個(gè)相反的派別。一方面,有些人堅(jiān)信 JUnit 的簡(jiǎn)單性對(duì)于不斷地提醒程 序員軟件也必須保持簡(jiǎn)單來(lái)說(shuō)是必不可少的(這稱為 KISS 原則,代表 keep it simple, stupid);另一方面,有些人認(rèn)為 JUnit 不是簡(jiǎn)單而是簡(jiǎn)化,所以他們想要從測(cè)試框架得到新的高級(jí)特性、更大的靈活性和更強(qiáng)大的能力。JUnit 的一些特殊特性,就是為了滿足這個(gè) 群體的一些具體批評(píng)而推出的:
因?yàn)?nbsp;Java 語(yǔ)言的單繼承性,所以必須擴(kuò)展 TestCase 類的限制很大。
無(wú)法向 JUnit 的測(cè)試方法傳遞參數(shù),也無(wú)法向 setUp() 和 tearDown() 方法傳遞參數(shù)。
執(zhí)行模型有點(diǎn)奇怪:每次執(zhí)行一個(gè)測(cè)試方法的時(shí)候,都要重新實(shí)例化測(cè)試類。
管理復(fù)雜項(xiàng)目中的不同測(cè)試套件有可能非常復(fù)雜。
TestNG 的創(chuàng)造者
TestNG 的創(chuàng)造者是 Cedric Beust,他在 Java 編程領(lǐng)域非常出名,是 EJB 3 專家組的成員,也是其他一些流行的開(kāi)源項(xiàng)目(例如 EJBGen 和 Doclipse)的創(chuàng)造者。TestNG 在 Apache 軟件許可條款約束下發(fā)布,并可從其 Web 站點(diǎn)下載(請(qǐng)參閱 參考 資料 中到該站點(diǎn)和 Cedric 站點(diǎn)的鏈接)。
在本文中,您將學(xué)習(xí)到如何用這個(gè)叫做 TestNG 的新測(cè)試框架 為應(yīng)用程序編寫(xiě)單元測(cè)試。TestNG 的靈感來(lái)自 JUnit,同時(shí)盡量保持后者的簡(jiǎn)單性;但是,TestNG 消除了老框架的大多數(shù)限制,使開(kāi)發(fā)人員 可以編寫(xiě)更加靈活、更加強(qiáng)大的測(cè)試。由于 TestNG 大量借用 Java Annotation(隨 JDK 5.0 引入;有關(guān)這個(gè)新特性的更多信 息,請(qǐng)參閱 參考資料)來(lái)定義測(cè)試,所以本文也可以向您演示如何在實(shí)際的生產(chǎn)環(huán)境中使用 Java 語(yǔ)言的這個(gè)新特性。
關(guān)于代碼
為 了演示 TestNG 的用法,我要為叫做 Jakarta Common Lang 的這個(gè)廣泛應(yīng)用的開(kāi)源庫(kù)(其中包含一些處理和操縱字符串、數(shù)字和 Java 對(duì)象的有用的類)編寫(xiě)一些單元測(cè)試。在下面的 參考資料 一節(jié)中,您可以找到 TestNG 和 Jakarta Common Lang 庫(kù)的鏈接;如果您想在自己的機(jī)器上隨著本文一起練習(xí),這二者都需要下載。
可以在兩個(gè)不同的包中得到 TestNG:一個(gè)包要求 JDK 5.0,另一個(gè)包與 Java 語(yǔ)言 1.4 版本兼容。定義測(cè)試的時(shí)候,它們使用的語(yǔ)法略有差異:前者使用 JDK 5.0 標(biāo)注,后者使用 舊的 Javadoc 風(fēng)格的標(biāo)注。本文使用的是 JDK 5.0 版本,所以在繼續(xù)閱讀本文之前,需要對(duì)標(biāo)注有基本的了解;您可以在 參考資料 中找到 關(guān)于這個(gè)主題的 developerWorks 資源的鏈接。但是,您要知道 只有在編譯和運(yùn)行測(cè)試的時(shí)候 才需要 JDK 5.0,所以您仍然可以用自 己喜歡的編譯器來(lái)構(gòu)建應(yīng)用程序。實(shí)際上,您將用從 Jakarata 項(xiàng)目的 Web 站點(diǎn)下載的相同 JAR 文件來(lái)測(cè)試 Jakarta Common Lang 庫(kù)。關(guān)于使用 Java 平臺(tái) 1.4 版本的 TestNG 的更多細(xì)節(jié),可以在 TestNG 的 Web 站點(diǎn)上找到。
最后,請(qǐng)單擊本文頂部或底部的 Code 圖標(biāo),下載 j-testng-sample.zip 文件,其中包 含一些示例,演示了如何用 TestNG 為 Jakarta Commons Lang 編寫(xiě)單元測(cè)試。在里面,可以找到這里給出的大多數(shù)代碼,還有其 他一些示例。閱讀本文并不需要這些代碼,但是它可以幫助您更加深入地理解在這里介紹的概念。
TestNG 快速起步
TestNG 的測(cè)試類是普通的老式 Java 對(duì)象;您不需要擴(kuò)展任何特殊的類,也不需要使用測(cè)試方法的任何命名約定:您只要用標(biāo)注 @Test 通知框架這個(gè)類的方 法是測(cè)試。清單 1 演示了實(shí)用類 StringUtils 的一個(gè)最簡(jiǎn)單的測(cè)試。它測(cè)試 StringUtils 的兩個(gè)方法: isEmpty() 方法檢測(cè) String 是否為空; trim() 方法從 String 兩端刪除控制字符。請(qǐng)注意,其中使用了 Java 指令 assert 來(lái)檢 測(cè)錯(cuò)誤情況。
清單 1. 針對(duì)類 StringUtils 的一個(gè)測(cè)試用例
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 "));
}
}
但 是,在運(yùn)行測(cè)試之前,必須用特殊的 XML 文件配置 TestNG,習(xí)慣上把這個(gè)文件命名為 testng.xml。這個(gè)文件的語(yǔ)法非常簡(jiǎn)單,如清單 2 所示。這個(gè)文件首先定義測(cè)試套件 My test suite,這個(gè)套件只包含一個(gè)測(cè)試 First test,這個(gè)測(cè)試由 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>
如 果這個(gè)示例 testng.xml 文件看起來(lái)沒(méi)什么用處(只有一個(gè)測(cè)試類),那么好消息是:這實(shí)際上是您定義測(cè)試套件時(shí) 惟一需要編寫(xiě)的文件。還記得 JUnit 過(guò)去的日子么?在那些日子里,套件的定義可能分布在多個(gè)文件中:JUnit 的 TestSuite 文件,屬性文件,還有當(dāng)然缺不了的 Ant 構(gòu)建文件。使用 TestNG,所有必需的數(shù)據(jù)都集中在 testng.xml 文件中。不需要額外的 TestSuite 文件和構(gòu)建文件。
要運(yùn)行測(cè)試,請(qǐng)用 javac 編譯類,然后用以下命令調(diào)用 TestNG :
java -ea -classpath .;testng.jar;commons-lang-2.0.jar com.beust.testng.TestNG testng.xml
在 這里,選項(xiàng) -ea 告訴 JVM 處理斷言(在斷言失敗時(shí)拋出異常);運(yùn)行這個(gè)例子只需要 testng.jar 和 commons-lang- 2.0.jar 這兩個(gè)庫(kù),而 com.beust.testng.TestNG 是 TestNG 的主類。對(duì)于所有那些已經(jīng)非常高興地忘記了 java 和 javac 的神秘語(yǔ)法的開(kāi)發(fā)人員來(lái)說(shuō),還提供了一個(gè)有用的 Ant 任務(wù)。作為例子,清單 3 演示了本文發(fā)布的示例應(yīng)用程序的 Ant 構(gòu)建文件。請(qǐng)注意與類 com.beust.testng.TestNGAntTask 關(guān)聯(lián)的 testng 任務(wù)的定義,以及它在 test 目標(biāo)中相當(dāng)簡(jiǎn)單的用法。
清單 3. 帶有 TestNG 任務(wù)的 Ant 構(gòu)建文件
<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>
如 果一切正常,那么應(yīng)當(dāng)在控制臺(tái)中看到測(cè)試結(jié)果。而且,TestNG 還在當(dāng)前目錄下自動(dòng)創(chuàng)建了一個(gè)叫做 test-output 的文件夾,并在其中創(chuàng)建 了一份非常好的 HTML 報(bào)告。如果打開(kāi)該報(bào)告并裝入 index.html,就可以看到與圖 1 中的頁(yè)面類似的頁(yè)面。
圖 1. TestNG 創(chuàng)建的 HTML 報(bào)告
定義測(cè)試組
TestNG 另外一個(gè)有趣的特性是其定義測(cè)試組的能力。每個(gè)測(cè)試方法都可以與一個(gè)或多個(gè)組相關(guān)聯(lián),但可以選擇只運(yùn)行某個(gè)測(cè)試組。要把測(cè)試加入測(cè)試組,只要把組指定為 @Test 標(biāo)注的參數(shù),使用的語(yǔ)法如下:
@Test(groups = {"tests.string"})
在 這個(gè)具體的例子中,您聲明:標(biāo)注的方法屬于 tests.string 組。因?yàn)閰?shù) groups 是一個(gè)數(shù)組,所以可以指定多個(gè)組,組名之間用逗號(hào)分 隔。例如,在示例應(yīng)用程序中,您可以為 String、Number 以及 boolean 創(chuàng)建不同的測(cè)試,然后如清單 4 所示配置 TestNG, 有選擇地運(yùn)行它們.
清單 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>
顯然,當(dāng)運(yùn)行不同的測(cè)試組時(shí),HTML 報(bào)告能夠在單一列表中顯示所有測(cè)試,也可以在獨(dú)立的列表中顯示每個(gè)組的測(cè)試,從而能夠立即理解問(wèn)題的來(lái)源。
配置方法
使用 TestNG,不僅可以指定測(cè)試方法,還可以用專門(mén)的標(biāo)注 @Configuration 指定類中的其他特定方法,這些方法叫做 配置方法。配置方法有四種類型:
beforeTestClass 方法在類實(shí)例化之后,但是在測(cè)試方法運(yùn)行之前執(zhí)行。
afterTestClass 方法在類中的所有測(cè)試方法執(zhí)行之后執(zhí)行。
beforeTestMethod 方法在類中的任何測(cè)試方法執(zhí)行之前執(zhí)行。
afterTestMethod 方法在類中的每個(gè)測(cè)試方法執(zhí)行之后執(zhí)行。
圖 2 進(jìn)一步描述了測(cè)試類的生命周期。
圖 2. 測(cè)試類的生命周期
清 單 5 演示了配置方法的一些示例。請(qǐng)注意,如果您使用組,那么配置方法也必須屬于某個(gè)組。而且,配置方法的四種類型彼此之間不是互斥的,所以可以把方法 定義成同時(shí)屬于一種或多種配置方法類型。(作為例子,請(qǐng)參閱清單 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() 方法的增強(qiáng)版;它們的主要目的是為測(cè)試創(chuàng)建正確的執(zhí)行上下文,并在測(cè)試用例執(zhí)行之后刷新數(shù)據(jù)。
異常檢測(cè)
使 用 TestNG,您可以非常簡(jiǎn)單、非常容易地檢測(cè)異常的發(fā)生。很明顯,用 JUnit 也可以做這件事,但是正如您在清單 6 中的示例中所看到的,使 用 TestNG 的 @ExpectedExceptions 標(biāo)注可以使代碼編寫(xiě)驚人地容易和簡(jiǎn)單。@ExpectedExceptions 標(biāo)注指 明框架能夠容忍拋出的 NumberFormatException 異常,所以不應(yīng)當(dāng)被當(dāng)作是故障。要查看在某行代碼中是否拋出異常,您可以直接在這行 代碼之后加入 assert false 語(yǔ)句。這意味著 只有 在指定行中拋出特定類型的異常的時(shí)候,您才會(huì)通過(guò)測(cè)試。
清單 6. 用 TestNG 進(jìn)行異常檢測(cè)
public class NumberUtilsTest
{
@Test(groups = {"tests.math"})
@ExpectedExceptions(NumberFormatException.class)
public void test()
{
NumberUtils.createDouble("12.23.45");
assert false; //shouldn't be invoked
}
}
結(jié)束語(yǔ)
在本文中,我提供了 TestNG 的快速實(shí)用介紹,目的是展示如何開(kāi)始編寫(xiě)單元測(cè)試。但是,它不是一份完整的參考手冊(cè)。TestNG 還有許多其他非常有用的有趣特性:
可以向測(cè)試方法和配置方法傳遞參數(shù),可以用標(biāo)注或在 XML 配置文件中聲明參數(shù)。
可以在 TestNG 下用“兼容模式”運(yùn)行過(guò)去運(yùn)行良好的 Junit 測(cè)試。
可以在測(cè)試組之間建立依賴性,決定它們的執(zhí)行順序。
要了解這個(gè)框架的所有潛力,有必要參閱 TestNG 的文檔(參閱 參考資料)。
所有這些特性,與用于定義測(cè)試的 Java 標(biāo)注一起,使整個(gè)測(cè)試過(guò)程更加簡(jiǎn)單、更加靈活。編寫(xiě)測(cè)試必須遵守的規(guī)則 很少;除此之外,您絕對(duì)可以自由選擇自己喜歡的測(cè)試策略。
在使用 TestNG 時(shí)最明顯的是,這個(gè)模板已經(jīng)是編寫(xiě)單元測(cè)試的一個(gè)好選擇了,而且,在設(shè)計(jì)上,它與其他庫(kù)和工具的集成非常簡(jiǎn)單,所以它未來(lái)的發(fā)展會(huì)給開(kāi)發(fā)人員帶來(lái)一些有趣的新東西。
參考資料
您可以參閱本文在 developerWorks 全球站點(diǎn)上的 英文原文。
請(qǐng)單本文頂部或底部的 Code 圖標(biāo)下載 j-testng-sample.zip,其中包含本文中討論的代碼。
在項(xiàng)目的 Web 站點(diǎn) 下載 TestNG。
運(yùn)行本文的代碼,需要下載 Jakarta Commons Lang 庫(kù)。
如果不熟悉 Java 標(biāo)注,請(qǐng)閱讀 Brett McLaughlin 2004 年 9 月份開(kāi)始在 developerWorks 上發(fā)表的由兩部分組成的系列文章:
Tiger 中的注釋,第 1 部分: 向 Java 代碼中添加元數(shù)據(jù)
Tiger 中的注釋,第 2 部分: 定制注釋
在 JUnit.org 下載 JUnit。
Malcolm Davis 撰寫(xiě)的“利用 Ant 和 JUnit 進(jìn)行增量開(kāi)發(fā)”(developerWorks,2000 年 11 月)是一篇很好的文章,解釋了如何把 JUnit 集成到項(xiàng)目中。
Eric Allen 和 Roy Miller 在他們各自的專欄 診斷 Java 代碼 和 極限編程 中都經(jīng)常提及到單元測(cè)試。
Testdriven.com 是關(guān)于測(cè)試驅(qū)動(dòng)開(kāi)發(fā)的文章和資源的全面集合。
學(xué)習(xí)更多關(guān)于各種 開(kāi)源測(cè)試工具。
學(xué)習(xí)更多關(guān)于“使用模仿對(duì)象進(jìn)行單元測(cè)試”,Alexander Day Chaffee 和 William Pietri (developerWorks,2002 年 11 月)。
關(guān)于 JUnit、單元測(cè)試和 TestNG 的大量思想都記錄在 Cedric Beust's blog。
您是否 test infected?了解這個(gè)術(shù)語(yǔ)的意義。
請(qǐng)?jiān)L問(wèn) Developer Bookstore,獲得技術(shù)書(shū)籍的完整列表,其中包括數(shù)百本 Java 相關(guān)主題 的圖書(shū)。
posted on 2007-11-04 14:08 百科 閱讀(197) 評(píng)論(0) 編輯 收藏