感謝原作者的奉獻,原作者博客地址:http://blog.csdn.net/zhu_ai_xin_520/article/details/6199194。
本次使用markdown重新制作一次,帶有些地方的修正。
1 - 簡介
TestNG 是一個測試框架,它被設計為用來簡化廣泛的設計需求,從單元測試 (單獨測試一個類) 到集成測試 (測試由多個類、報甚至外部框架,例如應用程序服務器,所組成的系統(tǒng)).
編寫一個測試通常分為三步:
- 編寫測試業(yè)務邏輯,并且在你的代碼中插入
TestNG annotations
. - 在
testng.xml
或build.xml
添加你的測試信息。例如類名,希望運行的組等等. - 運行TestNG.
你可以在歡迎
頁面上找到一個快速入門的例子。
文檔中會使用到如下的概念:
- 一套測試(suite)由一個XML文件所表示。它能夠包含一個或者多個測試, <suite> 標記來定義.
- 用 <test> 標記來表示一個測試,并且可以包含一個或者多個TestNG類.
- 所謂TestNG類,是一個Java類,它至少包含一個TestNG annotation.它由<class> 標記表示,并且可以包含一個或著多個測試方法。
- 測試方法,就是一個普通的Java方法,在由
@Test
標記.
TestNG測試可以通過@BeforeXXX
和@AfterXXX annotations
來標記,允許在特定的生命周期點上執(zhí)行一些Java邏輯,這些點可以是上述列表中任意的內容。
本手冊的其余部分將會解釋如下內容:
- 所有
annotation
的列表并且給出扼要的解釋。它會告訴你很多TestNG所提供的功能,但是你需要詳細閱讀其余部分來獲得關于這些注解更詳細的信息. - 關于
testng.xml
文件的說明。它的語法以及你能在里面寫什么. - 上述內容的詳細說明,同時也有結合了
annotation
和testing.xml
文件的使用說明.
2 - Annotations
如下就是在TestNG中可以使用的annotation的速查預覽,并且其中給出了屬性:
TestNG 類的配置信息 | |
---|---|
@BeforeSuite |
@BeforeSuite: 被注解的方法,會在當前suite 中所有測試方法之 前 被調用。 |
@AfterSuite | @AfterSuite: 被注解的方法,會在當前suite中所有測試方法之 后 被調用。 |
@BeforeTest | @BeforeTest: 被注解的方法,會在測試(原文就是測試,不是測試方法)運行 前 被調用 |
@AfterTest | @AfterTest: 被注解的方法,會在測試(原文就是測試,不是測試方法)運行后 被調用 |
@BeforeGroups | @BeforeGroups: 被注解的方法會在組列表中之前被調用。這個方法會在每個組中第一個測試方法被調用之前被調用 |
@AfterGroups | @AfterGroups: 被注解的方法會在組列表中之后被調用。這個方法會在每個組中最后一個測試方法被調用之后被調用 |
@BeforeClass | @BeforeClass: 被注解的方法,會在當前類第一個測試方法運行前被調用 |
@AfterClass | @AfterClass: 被注解的方法,會在當前類所有測試方法運行后被調用 |
@BeforeMethod | @BeforeMethod: 被注解的方法,會在運行每個測試方法之前調用 |
@AfterMethod | @AfterMethod: 被注解的方法,會在每個測試方法運行之后被調用 |
alwaysRun | 對于在方法之前的調用(beforeSuite, beforeTest, beforeTestClass 和 beforeTestMethod, 除了beforeGroups): 若為true ,這個配置方法無視其所屬的組而運行.對于在方法之后的調用(afterSuite, afterClass, ...): 若為true , 這個配置方法會運行,即使其之前一個或者多個被調用的方法失敗或者被跳過。 |
dependsOnGroups | 方法所依賴的一組group 列表 |
dependsOnMethods | 方法所依賴的一組method 列表 |
enabled | 在當前class/method 中被此annotation 標記的方法是否參與測試(不啟用則不在測試中運行) |
groups | 一組group 列表,指明了這個class/method 的所屬。 |
inheritGroups | 如果是true,則此方法會從屬于在類級由@Test 注解中所指定的組 |
@DataProvider | 把此方法標記為為測試方法提供數據的方法。被此注釋標記的方法必須返回Object[][] ,其中的每個Object[]可以被分配給測試方法列表中的方法當做參數。那些需要從DataProvider接受數據的@Test 方法,需要使用一個dataprovider 名字,此名稱必須與這個注解中的名字相同。 |
name | DataProvider的名字 |
@Factory | 把一個方法標記為工廠方法,并且必須返回被TestNG測試類所使用的對象們。 此方法必須返回 Object[]。 |
@Parameters | 說明如何給一個@Test 方法傳參。 |
value | 方法參數變量的列表 |
@Test | 把一個類或者方法標記為測試的一部分。 |
alwaysRun | 如果為true ,則這個測試方法即使在其所以來的方法為失敗時也總會被運行。 |
dataProvider | 這個測試方法的dataProvider
|
dataProviderClass | 指明去那里找data provider 類。如果不指定,那么就當前測試方法所在的類或者它個一個基類中去找。如果指定了,那么data provider 方法必須是指定的類中的靜態(tài)方法。 |
ependsOnGroups | 方法所依賴的一組grou p列表 |
dependsOnMethods | 方法所以來的一組method 列表 |
description | 方法的說明 |
enabled | 在當前class/method 中被此annotation 標記的方法是否參與測試(不啟用則不在測試中運行) |
expectedExceptions | 此方法會拋出的異常列表。如果沒有異常或者一個不在列表中的異常被拋出,則測試被標記為失敗。 |
groups | 一組group 列表,指明了這個class/method 的所屬。 |
invocationCount | 方法被調用的次數。 |
invocationTimeOut | 當前測試中所有調用累計時間的最大毫秒數。如果invocationCount 屬性沒有指定,那么此屬性會被忽略。 |
successPercentage | 當前方法執(zhí)行所期望的success 的百分比 |
sequential | 如果是true ,那么測試類中所有的方法都是按照其定義順序執(zhí)行,即使是當前的測試使用parallel="methods" 。此屬性只能用在類級別,如果用在方法級,就會被忽略。 |
timeOut | 當前測試所能運行的最大毫秒數 |
threadPoolSize | 此方法線程池的大小。 此方法會根據制定的invocationCount 值,以多個線程進行調用。注意:如果沒有指定invocationCount 屬性,那么此屬性就會被忽略
|
3 - testng.xml
調用TestNG有多種方式:
- 使用
testng.xml
文件 - 使用
ant
- 通過命令行
本節(jié)對testng.xml
的格式進行說明(你會在下文找到關于ant和命令行的相關文檔)。
目前給testng.xml所使用的DTD可以在主頁: http://testng.org/testng-1.0.dtd 上找到(考慮到您能更方便,可以瀏覽 HTML版)。
下面是個testng.xml
文件的例子:
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="Suite1" verbose="1" >
<test name="Nopackage" >
<classes>
<class name="NoPackageTest" />
</classes>
</test>
<test name="Regression1">
<classes>
<class name="test.sample.ParameterSample"/>
<class name="test.sample.ParameterTest"/>
</classes>
</test>
</suite>
你也可以指定包名來替代類名:
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="Suite1" verbose="1" >
<test name="Regression1" >
<packages>
<package name="test.sample" />
</packages>
</test>
</suite>
在這個例子中,TestNG會查看所有在test.sample
的類,并且只保留含有TestNG annotations
的類。
你也可以指定要包含和排除掉的組和方法:
<test name="Regression1">
<groups>
<run>
<exclude name="brokenTests" />
<include name="checkinTests" />
</run>
</groups>
<classes>
<class name="test.IndividualMethodsTest">
<methods>
<include name="testMethod" />
</methods>
</class>
</classes>
</test>
你也可以在testng.xml定義新的group,并且在屬性中指明額外的詳細信息,例如是否并行運行,使用多少個線程,并且是否正在運行JUnit測試等等……
默認情況下,TestNG按他們找到的XML文件中的順序運行測試。如果你想在這個文件中列出的類和方法的無序順序運行,可以設置preserve-order
屬性設置為false
。
<test name="Regression1" preserve-order="false">
<classes>
<class name="test.Test1">
<methods>
<include name="m1" />
<include name="m2" />
</methods>
</class>
<class name="test.Test2" />
</classes>
</test>
4 - 運行
TestNG可以使用多種方式調用:
- 命令行
- ant
- Eclipse
- IntelliJ's IDEA
本節(jié)將只介紹如何從命令行運行TestNG。如果您對其他方式感興趣,那么就點擊上面的鏈接查看更多信息。
假設TestNG已經在你的類路徑中,最簡單的調用方式如下:
java org.testng.TestNG testng1.xml [testng2.xml testng3.xml ...]
你至少要指定一個XML文件,它描述了你要運行的TestNG suite。此外,還有如下命令行參數:命令行參數
選項 | 參數 | 說明 |
---|---|---|
-d | 目錄 | 報告會生成的目錄 (默認是test-output ). |
-excludegroups | 逗號分隔的組列表 | 要在當前運行中被排除的組列表. |
-groups | 逗號分隔的組列表 | 想要運行的組(e.g. "windows,linux,regression" ). |
-listener | 逗號分隔的Java類列表,它們都可以在你的類路徑中找到 | 讓你指定你自己的監(jiān)聽器。這些類需要實現org.testng.ITestListener |
-parallel | 方法/測試 | 如果指定了,那么在運行測試的時候,所使用的默認的機制就會決定如何去使用并行 線程。反之則不會.這是可以在suite定義中被覆蓋的 |
-reporter | 自定義報告監(jiān)聽器的擴展配置 | 類似于-listener 選項,允許在報告器實例中配置JavaBean的樣式屬性.-reporter com.test.MyReporter:methodFilter=*insert*,enableFiltering=true 這個選項不限次數,根據需要一樣一個。 |
-sourcedir | 分號間隔的目錄列表 | 使用了JavaDoc類型的annotation 的源碼所在的目錄。這個選項只有你在使用JavaDoc類型的注解時才會有用。(e.g. "src/test" or"src/test/org/testng/eclipse-plugin;src/test/org/testng/testng") . |
-suitename |
test suite 默認的名字 |
指明了在命令行中定義的test suite 的名字。這個選項在suite.xml或源碼指定了不同的名字時會被忽略。如果使用雙引號括起來,就可在名字中使用空格。例如:"like this". |
-testclass | 逗號分隔的類列表,它們必須能在類路徑中被找到 | 逗號分隔的測試類的列表 (e.g. "org.foo.Test1,org.foo.test2") . |
-testjar | 一個jar文件 | 指定了一個包含了測試類的Jar文件。如果testng.xml 在jar文件的根目錄被找到,就使用之,反之,jar文件中所有的類都會被當成測試類。 |
-testname | 測試所使用的默認名字 | 它為在命令行中定義的測試指定了名字。這個選項在suite.xml或源碼指定了不同的名字時會被忽略。如果使用雙引號括起來,就可在名字中使用空格。例如:"like this"。 |
-testrunfactory | 可以在類路徑中找到的Java類 | 讓你指定你自己的測試運行器,相關的類必須實現org.testng.ITestRunnerFactory. |
-threadcount | 在并行測試的時候默認使用的線程數 | 并行運行中所使用的最大線程數。只在使用并行模式中有效(例如,使用-parallel選項)。它可以在suite定義中被覆蓋。 |
上面的參數說明可以通過不帶任何參數運行TestNG來獲得。
你也可以把命令行開關放到文件中,例如說c:/command.txt
,之后告訴 TestNG 使用這個文件來解析其參數:
C:> more c:\command.txt
-d test-output testng.xml
C:> java org.testng.TestNG @c:\command.txt
此外TestNG也可以在命令行下向其傳遞JVM參數。例如:
java -Dtestng.test.classpath="c:/build;c:/java/classes;" org.testng.TestNG testng.xml
如下是TestNG所能理解的屬性:
屬性 | 類型 | 說明 |
---|---|---|
testng.test.classpath | 分號分的一系列目錄,其中包含了你的測試類 | 如果指定了這個屬性,TestNG就會查找你的測試類而不是類路徑。這在你的類路徑中有很多類,而大多數又不是測試類,或者在xml文件中使用 package 標記的時候會很方便。 |
例子:
java org.testng.TestNG -groups windows,linux -testclass org.test.MyTest
注意ant task和testng.xml允許你使用更多的參數來運行TestNG(要包含的方法、指定的參數等等),所以你在學習TestNG的時候考慮使用命令行,因為這樣能讓你快速進步。
5 - 測試方法、測試類和測試組
5.1 - 測試方法
測試方法被注解為@Test。注釋的方法與@Test碰巧返回值將被忽略,除非你設置允許回值設置為true
在你的testng.xml
:
<suite allow-return-values="true">
or
<test allow-return-values="true">
5.2 - 測試組
TestNG 允許你將測試方法歸類為不同的組。不僅僅是可以聲明某個方法屬于某個組,而且還可以讓組包含其他的組。這樣TestNG可以調用或者請求包含一組特定的組(或者正則表達式)而排除其他不需要組的集合。這樣,如果你打算將測試分成兩份的時候,就無需重新編譯。這個特點,會給你在劃分組的時候帶來很大的靈活性。
組在的testng.xml文件中指定,可以發(fā)現無論是<test>
或<suite>
標簽。指定組<suite>
標簽適用于所有的<test>
下方標簽。需要注意的是群體標簽:如果指定組的“a”在<suite>
標簽中和b在<test>
標簽中,那么這兩個“a”和“b”將被包括在內。
例如,通常將測試劃分為兩種類別是再常見不過的了:
- 檢查性測試(Check-in test):這些測試在你提交新代碼之前就會運行。它們一般都是很快進行的,并且保證沒有哪個基本的功能是不好使的。
- 功能性測試(Functional test):這些測試涵蓋你的軟件中所有的功能,并且至少每天運行一次,不過你也可能希望他們持續(xù)的運行。
典型的來說,檢測性測試通常是功能性測試的一個子集。TestNG允許你根據個人感覺來進行組劃分。例如,你可能希望把你所有的測試類都劃歸為"functest"
組,并且額外的有幾個方法輸入"checkintest"
組。
public class Test1 {
@Test(groups = { "functest", "checkintest" })
public void testMethod1() {
}
@Test(groups = {"functest", "checkintest"} )
public void testMethod2() {
}
@Test(groups = { "functest" })
public void testMethod3() {
}
}
通過下面的內容調用TestNG
<test name="Test1">
<groups>
<run>
<include name="functest"/>
</run>
</groups>
<classes>
<class name="example1.Test1"/>
</classes>
</test>
以上會運行上面那個類中所有的測試,當藥使用checkintest進行調用的時候,就僅僅運行testMethod1()和testMethod2()。
下面是另外一個例子。這次使用正則表達式。假定有些測試方法不應該運行在Linux環(huán)境下,你的測試會看起來像:
@Test
public class Test1 {
@Test(groups = { "windows.checkintest" })
public void testWindowsOnly() {
}
@Test(groups = {"linux.checkintest"} )
public void testLinuxOnly() {
}
@Test(groups = { "windows.functest" )
public void testWindowsToo() {
}
}
然后你就可以使用下面這個 testng.xml 來只運行在Windows下的方法:
<test name="Test1">
<groups>
<run>
<include name="windows.*"/>
</run>
</groups>
<classes>
<class name="example1.Test1"/>
</classes>
</test>
注意:TestNG使用的是正則表達式
,而不是通配符
。注意這二者的區(qū)別(例如,"anything" 是匹配于 "."代表點和星號,而不是星號 "").
Method groups
也可以單獨排除和包含若干方法:
<test name="Test1">
<classes>
<class name="example1.Test1">
<methods>
<include name=".*enabledTestMethod.*"/>
<exclude name=".*brokenTestMethod.*"/>
</methods>
</class>
</classes>
</test>
這樣就可以不用編譯而處理任何不需要的方法了,但是我不推薦過分的使用這個技巧,因為如果你要重構你的代碼,那么這有可能讓你的測試框架出問題(在標簽中使用的方法可能再也不會匹配你的方法名了)。
5.3 - 組中組
測試組也可以包含其他組。這樣的組叫做“元組"(MetaGroups)"
。例如,你可能要定義一個組"all"
來包含其他的組,"chekcintest"
和"functest"
。"functest"
本身只包含了組windows和linux,而"checkintest"
僅僅包含windows。你就可以在屬性文件中這樣定義:
<test name="Regression1">
<groups>
<define name="functest">
<include name="windows"/>
<include name="linux"/>
</define>
<define name="all">
<include name="functest"/>
<include name="checkintest"/>
</define>
<run>
<include name="all"/>
</run>
</groups>
<classes>
<class name="test.sample.Test1"/>
</classes>
</test>
5.4 - 排除組
TestNG 允許你包含組,當然也可以排除之。
譬如說,因為最近的改動,導致當前的測試中斷并且,你還沒有時間修復這些問題都是司空見慣的。但是,你還需要自己的功能測試可以正確運行,所以,制藥簡單的讓這些不需要的測試失效就可以了。但是別忘記在以后需要的時候,要重新讓其生效。
一個簡單的辦法來解決這個問題就是創(chuàng)建一個叫做"broken"
組, 然后使得這些測試方法從屬于那個組。例如上面的例子,假設我知道testMethod2()
會中斷,所以我希望使其失效:
@Test(groups = {"checkintest", "broken"} )
public void testMethod2() {
}
而我所需要做的一切就是從運行中排除這個組:
<test name="Simple example">
<groups>
<run>
<include name="checkintest"/>
<exclude name="broken"/>
</run>
</groups>
<classes>
<class name="example1.Test1"/>
</classes>
</test>
通過這種辦法,我們既可以得到整潔的測試運行,同時也能夠跟蹤那些需要稍后修正的中斷的測試。
注意:你可以可以通過使用"enabled"
屬性來完成,這個屬性適用于@Test
和 @Before/After annotation
。
5.5 - 局部組
可以在類級別定義組,之后還可以在方法級定義組:
@Test(groups = { "checkin-test" })
public class All {
@Test(groups = { "func-test" )
public void method1() { ... }
public void method2() { ... }
}
在這個類中,method2()
類級組"checkin-test"
的一部分,而method1()
即屬于"checkin-test"
也屬于"func-test"
組。
5.6 - 參數
測試方法是可以帶有參數的。每個測試方法都可以帶有任意數量的參數,并且可以通過使用TestNG的@Parameters
向方法傳遞正確的參數。
設置方式有兩種方法:使用testng.xml
或者程序編碼。
5.6.1 - 使用 testng.xml 設置參數
如果只使用相對簡單的參數,你可以在你的testng.xml
文件中指定:
@Parameters({ "first-name" })
@Test
public void testSingleString(String firstName) {
System.out.println("Invoked testString " + firstName);
assert "Cedric".equals(firstName);
}
在這段代碼中,我們讓 firstName 參數能夠接到XML文件中叫做 first-name
參數的值。這個XML參數被定義在 testng.xml
:
<suite name="My suite">
<parameter name="first-name" value="Cedric"/>
<test name="Simple example">
<-- ... -->
類似的,它也可以用在@Before/After
和@Factory
注解上:
@Parameters({ "datasource", "jdbcDriver" })
@BeforeMethod
public void beforeTest(String ds, String driver) {
m_dataSource = ...; // 查詢數據源的值
m_jdbcDriver = driver;
}
這次有兩個Java參數ds
和driver
會分別接收到來自屬性datasource``和
jdbc-driver`所指定的值。
參數也可以通過 Optional 注釋來聲明:
@Parameters("db")
@Test
public void testNonExistentParameter(@Optional("mysql") String db) { ... }
如果在你的testng.xml
文件中沒有找到"db"
,你的測試方法就會使用 @Optional
中的值:"mysql"
。
@Parameters
可以被放置到如下位置:
- 在任何已經被
@Test
,@Before/After
或@Factory
注解過的地方。 - 在測試類中至多被放到一個構造函數簽。這樣,TestNG才能在需要的時候使用 testng.xml 中特定的參數來實例化這個類。這個特性可以被用作初始化某些類中的值,以便稍后會被類中其他的方法所使用。
注意:
- XML中的參數會按照Java參數在注解中出現的順序被映射過去,并且如果數量不匹配,TestNG會報錯。
- 參數是有作用范圍的。在
testng.xml
中,你即可以在<suite> 標簽下聲明,也可以在 <test>下聲明。如果兩個參數都有相同的名字,那么,定義在 <test> 中的有優(yōu)先權。這在你需要覆蓋某些測試中特定參數的值時,會非常方便。
5.6.2 - 使用DataProviders提供參數
在 testng.xml 中指定參數可能會有如下的不足:
如果你壓根不用testng.xml
.
你需要傳遞復雜的參數,或者從Java中創(chuàng)建參數(復雜對象,對象從屬性文件或者數據庫中讀取的etc...)
這樣的話,你就可以使用Data Provider
來給需要的測試提供參數。所謂數據提供者,就是一個能返回對象數組的數組的方法,并且這個方法被@DataProvider注解標注:
//這個方法會服務于任何把它(測試方法)的數據提供者的名字為"test1"方法
@DataProvider(name = "test1")
public Object[][] createData1() {
return new Object[][] {
{ "Cedric", new Integer(36) },
{ "Anne", new Integer(37)},
};
}
//這個測試方法,聲明其數據提供者的名字為“test1”
@Test(dataProvider = "test1")
public void verifyData1(String n1, Integer n2) {
System.out.println(n1 + " " + n2);
}
結果會打印
Cedric 36
Anne 37
被@Test
標注的方法通過dataProvider
屬性指明其數據提供商。這個名字必須與@DataProvider(name="...")
中的名字相一致。
默認的情況下,數據提供者會查找當前的測試類或者測試類的基類。如果你希望它能夠被其他的類所使用,那么就要將其指定為static
,并且通過 dataProviderClass
屬性指定要使用的類:
public class StaticProvider {
@DataProvider(name = "create")
public static Object[][] createData() {
return new Object[][] {
new Object[] { new Integer(42) }
}
}
}
public class MyTest {
@Test(dataProvider = "create", dataProviderClass = StaticProvider.class)
public void test(Integer n) {
// ...
}
}
Data Provider
方法可以返回如下兩種類型中的一種:
- 含有多個對象的數組
(Object[][])
,其中第一個下標指明了測試方法要調用的次數,第二個下標則完全與測試方法中的參數類型和個數相匹配。上面的例子已經說明。 - 另外一個是迭代器
Iterator<Object[]>
。二者的區(qū)別是迭代器允許你延遲創(chuàng)建自己的測試數據。TestNG會調用迭代器,之后測試方法會一個接一個的調用由迭代器返回的值。在你需要傳遞很多參數組給測試組的時候,這樣你無須提前創(chuàng)建一堆值。
下面是使用這個功能的例子:
@DataProvider(name = "test1")
public Iterator<Object[]> createData() {
return new MyIterator(DATA);
}
如果你聲明的@DataProvider
使用java.lang.reflect.Method
作為第一個參數,TestNG 會把當前的測試方法當成參數傳給第一個參數。這一點在你的多個測試方法使用相同的@DataProvider
的時候,并且你想要依據具體的測試方法返回不同的值時,特別有用。
例如,下面的代碼它內部的@DataProvider
中的測試方法的名字:
@DataProvider(name = "dp")
public Object[][] createData(Method m) {
System.out.println(m.getName()); // print test method name
return new Object[][] { new Object[] { "Cedric" }};
}
@Test(dataProvider = "dp")
public void test1(String s) {
}
@Test(dataProvider = "dp")
public void test2(String s) {
}
所以會顯示:
test1
test2
Data provider
可以通過屬性parallel
實現并行運行:
@DataProvider(parallel = true)
// ...
使用XML文件運行的data provider
享有相同的線程池,默認的大小是10.你可以通過修改該在<suite>
標簽中的值來更改:
<suite name="Suite1" data-provider-thread-count="20" >
...
如果你需要讓指定的幾個data provider
運行在不同的線程中,那么就必須通過不同的xml文件來運行。
5.6.3 - 在報告中的參數
在測試方法調用中所使用的參數,會在由TestNG中生成HTML報告里顯示出來。下面是幾個例子:

5.7 - 依賴方法
有些時候,需要按照特定順序調用測試方法。對于下面的情況,這非常有用:
- 在運行更多測試方法之前確保一定數量的測試方法已經完成并成功
- 要初始化的測試,同時希望這個初始化方法是測試方法,以及(標有@Before/After的方法不會最終報告的一部分)。
TestNG的允許您指定的依賴要么注解或XML格式。
5.7.1 - 依賴和注釋
確保在進行更多的方法測試之前,有一定數量的測試方法已經成功完成。
在初始化測試的時候,同時希望這個初始化方法也是一個測試方法(@Before/After
不會出現在最后生成的報告中)。
為此,你可以使用@Test
中的dependsOnMethods
或dependsOnGroups
屬性。
這兩種依賴:
- Hard dependencies(硬依賴)。所有的被依賴方法必須成功運行。只要有一個出問題,測試就不會被調用,并且在報告中被標記為SKIP。
- Soft dependencies(軟依賴)。 即便是有些依賴方法失敗了,也一樣運行。如果你只是需要保證你的測試方法按照順序執(zhí)行,而不關心他們的依賴方法是否成功。那么這種機制就非常有用。可以通過添加
"alwaysRun=true"
到@Test
來實現軟依賴。
硬依賴的例子:
@Test
public void serverStartedOk() {}
@Test(dependsOnMethods = { "serverStartedOk" })
public void method1() {}
此例中,method1()
依賴于方法serverStartedOk()
,從而保證 serverStartedOk()
總是先運行。
也可以讓若干方法依賴于組:
@Test(groups = { "init" })
public void serverStartedOk() {}
@Test(groups = { "init" })
public void initEnvironment() {}
@Test(dependsOnGroups = { "init.* })
public void method1() {}
本例中,method1()``依賴于匹配正則表達式
"init.*"的組,由此保證了
serverStartedOk()和
initEnvironment()總是先于
method1()`被調用。
注意:正如前面所說的那樣,在相同組中的調用可是在夸測試中不保證順序的。
如果你使用硬依賴,并且被依賴方法失敗(alwaysRun=false
,即默認是硬依賴),依賴方法則不是被標記為FAIL
而是SKIP
。被跳過的方法會被在最后的報告中標記出來(HTML既不用紅色也不是綠色所表示),主要是被跳過的方法不是必然失敗,所以被標出來做以區(qū)別。
無論dependsOnGroups
還是dependsOnMethods
都可以接受正則表達式作為參數。對于dependsOnMethods
,如果被依賴的方法有多個重載,那么所有的重載方法都會被調用。如果你只希望使用這些重載中的一個,那么就應該使用 dependsOnGroups
。
更多關于依賴方法的例子,請參考這篇文章,其中也包含了對多重依賴使用繼承方式來提供一種優(yōu)雅的解決方案。
默認情況下,相關的方法是按類分組。例如,如果方法a()
依賴于方法的b()
和你有一個包含這些方法的類的幾個實例(數據工廠提供的數據),這些方法的類的幾個實例,然后調用順序將如下:
a(1)
a(2)
b(2)
b(2)
TestNG的不會運行b()
,直到所有實例都調用它們的a()
方法。
這種行為可能不希望在某些情況下,例如在測試的標志并簽署了Web瀏覽器的各個國家。在這種情況下,你希望下面的順序:
signIn("us")
signOut("us")
signIn("uk")
signOut("uk")
對于這個順序,你可以使用XML屬性組group-by-instances
。這個屬性可以是在<suite>或<test>有效:
<suite name="Factory" order-by-instances="true">
or
<test name="Factory" order-by-instances="true">
5.7.2 - 在XML中依賴的關系
另外,您也可以在的testng.xml
文件中指定的組依賴關系。您可以使用<dependencies>標簽來實現這一點:
<test name="My suite">
<groups>
<dependencies>
<group name="c" depends-on="a b" />
<group name="z" depends-on="c" />
</dependencies>
</groups>
</test>
5.8 - 工廠
工廠允許你動態(tài)的創(chuàng)建測試。例如,假設你需要創(chuàng)建一個測試方法,并用它來多次訪問一個web頁面,而且每次都帶有不同的參數:
public class TestWebServer {
@Test(parameters = { "number-of-times" })
public void accessPage(int numberOfTimes) {
while (numberOfTimes-- > 0) {
// access the web page
}
}
}
<test name="T1">
<parameter name="number-of-times" value="10"/>
<class name= "TestWebServer" />
</test>
<test name="T2">
<parameter name="number-of-times" value="20"/>
<class name= "TestWebServer"/>
</test>
<test name="T3">
<parameter name="number-of-times" value="30"/>
<class name= "TestWebServer"/>
</test>
參數一旦多起來,就難以管理了,所以應該使用工廠來做:
public class WebTestFactory {
@Factory
public Object[] createInstances() {
Object[] result = new Object[10];
for (int i = 0; i < 10; i++) {
result[i] = new WebTest(i * 10);
}
return result;
}
}
新的測試類如下:
public class WebTest {
private int m_numberOfTimes;
public WebTest(int numberOfTimes) {
m_numberOfTimes = numberOfTimes;
}
@Test
public void testServer() {
for (int i = 0; i < m_numberOfTimes; i++) {
// access the web page
}
}
}
你的testng.xml
只需要引用包含工廠方法的類,而測試實例自己會在運行時創(chuàng)建:
<class name="WebTestFactory" />
工廠方法可以接受諸如@Test
和@Before/After
所標記的參數,并且會返回 Object[]
。這些返回的對象可以是任何類(不一定是跟工廠方法相同的類),并且他們甚至都不需要TestNG注解(在例子中會被TestNG忽略掉)
工廠也可以被應用于數據提供者,并可以通過將@Factory
注釋無論是在普通的方法或構造函數利用此功能。這里是一個構造工廠的例子:
@Factory(dataProvider = "dp")
public FactoryDataProviderSampleTest(int n) {
super(n);
}
@DataProvider
static public Object[][] dp() {
return new Object[][] {
new Object[] { 41 },
new Object[] { 42 },
};
}
該示例將TestNG的創(chuàng)建兩個測試類,這個構造函數將調用值為41和42的對像。
5.9 - 類級注解
通常 @Test 也可以用來標注類,而不僅僅是方法:
@Test
public class Test1 {
public void test1() {
}
public void test2() {
}
}
處于類級的@Test
會使得類中所有的public
方法成為測試方法,而不管他們是否已經被標注。當然,你仍然可以用@Test
注解重復標注測試方法,特別是要為其添加一些特別的屬性時。
例如:
@Test
public class Test1 {
public void test1() {
}
@Test(groups = "g1")
public void test2() {
}
}
上例中test1()
和test2()
都被處理,不過在此之上test2()
現在還屬于組 "g1"。
5.10 - 并行運行和超時
您可以以各種方式指示TestNG單獨線程運行測試。
5.10.1 - 并行套件
如果你正在運行幾個組件文件(e.g. "java org.testng.TestNG testng1.xml testng2.xml"
)和想讓這些組件以一個獨立的線程運行這個是非常有用的。你可以使用以下命令行參數來指定線程池的大小。
java org.testng.TestNG -suitethreadpoolsize 3 testng1.xml testng2.xml testng3.xml
相應的ant任務名稱為suitethreadpoolsize
。
5.10.2 - 并行測試,類和方法
你可以通過在suite標簽中使用 parallel 屬性來讓測試方法運行在不同的線程中。這個屬性可以帶有如下這樣的值:
<suite name="My suite" parallel="methods" thread-count="5">
<suite name="My suite" parallel="tests" thread-count="5">
<suite name="My suite" parallel="classes" thread-count="5">
<suite name="My suite" parallel="instances" thread-count="5">
parallel="methods": TestNG 會在不同的線程中運行測試方法,除非那些互相依賴的方法。那些相互依賴的方法會運行在同一個線程中,并且遵照其執(zhí)行順序。
parallel="tests": TestNG 會在相同的線程中運行相同的<test>標記下的所有方法,但是每個<test>標簽中的所有方法會運行在不同的線程中。這樣就允許你把所有非線程安全的類分組到同一個<test>標簽下,并且使其可以利用TestNG多線程的特性的同時,讓這些類運行在相同的線程中。
parallel="classes": TestNG 會在相同線程中相同類中的運行所有的方法,但是每個類都會用不同的線程運行。
parallel="instances": TestNG會在相同線程中相同實例中運行所有的方法,但是兩個不同的實例將運行在不同的線程中。
此外,屬性thread-count
允許你為當前的執(zhí)行指定可以運行的線程數量。
注意:@Test 中的屬性timeOut
可以工作在并行和非并行兩種模式下。
你也可以指定@Test
方法在不同的線程中被調用。你可以使用屬性 threadPoolSize
來實現:
@Test(threadPoolSize = 3, invocationCount = 10, timeOut = 10000)
public void testServer() {
上例中,方法testServer
會在3個線程中調用10次。此外,10秒鐘的超時設定也保證了這三個線程中的任何一個都永遠不會阻塞當前被調用的線程。
5.11 - 再次運行失敗的測試
每次測試suite
出現失敗的測試,TestNG 就會在輸出目錄中創(chuàng)建一個叫做 testng-failed.xml
的文件。這個XML文件包含了重新運行那些失敗測試的必要信息,使得你可以無需運行整個測試就可以快速重新運行失敗的測試。所以,一個典型的會話看起來像:
要注意的是,testng-failed.xml
已經包含了所有失敗方法運行時需要的依賴,所以完全可以保證上次失敗的方法不會出現任何 SKIP。
5.12 - JUnit測試
TestNG 能夠運行 JUnit 測試。所有要做的工作就是在testng.classNames
屬性中設定要運行的JUnit測試類,并且把testng.junit
屬性設置為true
:
<test name="Test1" junit="true">
<classes>
<!-- ... -->
TestNG 在這種情況下所表現的行為與 JUnit 相似:
-
junit3
- 所有類中要運行的測試方法由 test* 開頭
- 如果類中有 setUp() 方法,則其會在每個測試方法執(zhí)行前被調用
- 如果類中有 tearDown() 方法,則其會在每個測試方法之后被調用
- 如果測試類包含 suite() 方法,則所有的被這個方法返回的測試類都會被調用
-
junit4
- TestNG將使用
org.junit.runner.JUnitCore
運行器運行你的測試。
- TestNG將使用
5.13 - 程序化運行TestNG
你可以在程序中非常輕松的調用TestNG的測試:
TestListenerAdapter tla = new TestListenerAdapter();
TestNG testng = new TestNG();
testng.setTestClasses(new Class[] { Run2.class });
testng.addListener(tla);
testng.run();
本例中創(chuàng)建了一個TestNG
對象,并且運行測試類Run2
。它添加了一個 TestListener
(這是個監(jiān)聽器) 。你既可以使用適配器類 org.testng.TestListenerAdapter
來做,也可以實現org.testng.ITestListener
接口。這個接口包含了各種各樣的回調方法,能夠讓你跟蹤測試什么時候開始、成功、失敗等等。
類似的你可以用testng.xml
文件調用或者創(chuàng)建一個虛擬的testng.xml
文件來調用。為此,你可以使用這個包org.testng.xml
中的類:XmlClass、XmlTest
等等。每個類都對應了其在xml中對等的標簽。
例如,假設你要創(chuàng)建下面這樣的虛擬文件:
<suite name="TmpSuite" >
<test name="TmpTest" >
<classes>
<class name="test.failures.Child" />
<classes>
</test>
</suite>
你需要使用如下代碼:
XmlSuite suite = new XmlSuite();
suite.setName("TmpSuite");
XmlTest test = new XmlTest(suite);
test.setName("TmpTest");
List<XmlClass> classes = new ArrayList<XmlClass>();
classes.add(new XmlClass("test.failures.Child"));
test.setXmlClasses(classes) ;
之后你可以傳遞這個XmlSuite
給 TestNG:
List<XmlSuite> suites = new ArrayList<XmlSuite>();
suites.add(suite);
TestNG tng = new TestNG();
tng.setXmlSuites(suites);
tng.run();
請參閱JavaDocs來獲取完整的API。
5.14 - BeanShell于高級組選擇
如果<include>
和<exclude>
不夠用,那就是用BeanShell
表達式來決定是否一個特定的測試方法應該被包含進來。只要在<test>
標簽下使用這個表達式就好了:
<test name="BeanShell test">
<method-selectors>
<method-selector>
<script language="beanshell"><![CDATA[
groups.containsKey("test1")
]]></script>
</method-selector>
</method-selectors>
<!-- ... -->
當在testng.xml
文件中找到<script>
標簽后,TestNG 就會忽略在當前<test>標簽內組和方法的的<include>
和<exclude>
標簽:BeanShell
就會成為測試方法是否被包含的唯一決定因素。
下面是關于BeanShell 腳本的額外說明:
- 必須返回一個
boolean
值。除了這個約束以外,任何有效的BeanShell
代碼都是允許的 (例如,可能需要在工作日返回true
而在周末返回false
,這樣就允許你可以依照不同的日期進行測試)。 - TestNG 定義了如下的變量供你調用:
java.lang.reflect.Method method: 當前的測試方法
org.testng.ITestNGMethod testngMethod: 當前測試方法的描述
java.util.Map<String, String> groups: 當前測試方法所屬組的映射 - 你也可能需要使用 CDATA 聲明來括起B(yǎng)eanshell表達式(就像上例)來避免對XML保留字冗長的引用
5.15 - 注解轉換器
TestNG 允許你在運行時修改所有注解的內容。在源碼中注解大多數時候都能正常工作的時時非常有用的(這句原文就是這意思,感覺不太對勁),但是有幾個情況你可能會改變其中的值。
為此,你會用到注解轉換器( Annotation Transformer )。
所謂注解轉換器,就是實現了下面接口的類:
public interface IAnnotationTransformer {
/**
* This method will be invoked by TestNG to give you a chance
* to modify a TestNG annotation read from your test classes.
* You can change the values you need by calling any of the
* setters on the ITest interface.
*
* Note that only one of the three parameters testClass,
* testConstructor and testMethod will be non-null.
*
* @param annotation The annotation that was read from your
* test class.
* @param testClass If the annotation was found on a class, this
* parameter represents this class (null otherwise).
* @param testConstructor If the annotation was found on a constructor,
* this parameter represents this constructor (null otherwise).
* @param testMethod If the annotation was found on a method,
* this parameter represents this method (null otherwise).
*/
public void transform(ITest annotation, Class testClass,
Constructor testConstructor, Method testMethod);
}
像其他的TestNG 監(jiān)聽器,你可以指定在命令行或者通過ant
來指定這個類:
java org.testng.TestNG -listener MyTransformer testng.xml
或者在程序中:
TestNG tng = new TestNG();
tng.setAnnotationTransformer(new MyTransformer());
// ...
當調用transform()
的時候,你可以調用任何在 ITest test 參數中的設置方法來在進一步處理之前改變它的值。
例如,這里是你如何覆蓋屬性invocationCount
的值,但是只有測試類中的invoke()
方法受影響:
public class MyTransformer implements IAnnotationTransformer {
public void transform(ITest annotation, Class testClass,
Constructor testConstructor, Method testMethod)
{
if ("invoke".equals(testMethod.getName())) {
annotation.setInvocationCount(5);
}
}
}
IAnnotationTransformer
只允許你修改一個@Test
注解。如果你需要修改其他的(假設說配置注解@Factory
或@DataProvider
),使用 IAnnotationTransformer2
。
5.16 - 方法攔截器
一旦TestNG 計算好了測試方法會以怎樣的順序調用,那么這些方法就會分為兩組:
- 按照順序運行的方法。這里所有的方法都有相關的依賴,并且所有這些方法按照特定順序運行。
- 不定順序運行的方法。這里的方法不屬于第一個類別。方法的運行順序是隨機的,下一個說不準是什么(盡管如此,默認情況下TestNG會嘗試通過類來組織方法)。
為了能夠讓你更好的控制第二種類別,TestNG定義如下接口:
public interface IMethodInterceptor {
List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context);
}
方法中叫做methods的那個列表參數包含了所有以不定序運行的方法。你的 intercept
方法也要返回一個IMethodInstance
列表,它可能是下面情況之一:
- 內容與參數中接收的一致,但是順序不同
- 一組
IMethodInstance
對象 - 更大的一組
IMethodInstance
對象
一旦你定義了攔截器,就把它傳遞個TestNG,用下面的方式:
java -classpath "testng-jdk15.jar:test/build" org.testng.TestNG -listener test.methodinterceptors.NullMethodInterceptor
-testclass test.methodinterceptors.FooTest
關于 ant 中對應的語法,參見listeners
屬性ant文檔 中的說明。
例如,下面是個方法攔截器會重新給方法排序,一遍“fast”
組中的方法總是先執(zhí)行:
public List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context) {
List<IMethodInstance> result = new ArrayList<IMethodInstance>();
for (IMethodInstance m : methods) {
Test test = m.getMethod().getConstructorOrMethod().getAnnotation(Test.class);
Set<String> groups = new HashSet<String>();
for (String group : test.groups()) {
groups.add(group);
}
if (groups.contains("fast")) {
result.add(0, m);
}
else {
result.add(m);
}
}
return result;
}
5.17 - TestNG 監(jiān)聽器
有很多接口可以用來修改 TestNG 的行為。這些接口都被統(tǒng)稱為 "TestNG 監(jiān)聽器"。下面是目前支持的監(jiān)聽器的列表:
- IAnnotationTransformer (doc, javadoc)
- IReporter (doc, javadoc)
- ITestListener (doc, javadoc)
- IMethodInterceptor (doc, javadoc)
- IInvokedMethodListener (doc, javadoc)
當你實現了這些接口,你可以讓 TestNG 知道這些接口,有如下方式:
- 在命令行下使用
-listener
- ant中使用
<listeners>
- 在
testng.xml
中使用<listeners>
標簽 - 在測試類上述用
@Listeners
注釋 - 使用
ServiceLoader
5.17.1 - 在testng.xml或者java中制定監(jiān)聽
下面就是你如何在testng.xml中定義監(jiān)聽:
<suite>
<listeners>
<listener class-name="com.example.MyListener" />
<listener class-name="com.example.MyMethodInterceptor" />
</listeners>
...
或者你喜歡在java中定義的監(jiān)聽:
@Listeners({ com.example.MyListener.class, com.example.MyMethodInterceptor.class })
public class MyTest {
// ...
}
該@Listeners
注釋可以包含擴展org.testng.ITestNGListener
除了IAnnotationTransformer
和IAnnotationTransformer2
任何類。其原因是,這些監(jiān)聽需要在過程中很早就知道以便TestNG可以使用它們來重寫你的注解,因此,你需要在你的testng.xml
文件中指定這些偵聽器。
請注意@Listeners
注釋將適用于整個套件的文件,就像如果你已經指定在一個的testng.xml
文件。如果你想限制它的范圍(例如,僅運行在當前類),在監(jiān)聽的代碼可以先檢查測試方法的運行,并決定該怎么做呢。
5.17.2 - 書寫服務加載監(jiān)聽
最后,JDK中提供了一個非常優(yōu)雅的機制,通過ServiceLoader類的借口路徑來實現監(jiān)聽。
隨著ServiceLoader
,這一切你所需要做的就是創(chuàng)建一個包含你的監(jiān)聽和一些配置文件的JAR文件,把這個jar文件放在classpath中運行TestNG并TestNG會自動找到它們。
下面是它如何工作的具體例子。
讓我們先創(chuàng)建一個偵聽器(所有的TetstNG監(jiān)聽都應該響應):
package test.tmp;
public class TmpSuiteListener implements ISuiteListener {
@Override
public void onFinish(ISuite suite) {
System.out.println("Finishing");
}
@Override
public void onStart(ISuite suite) {
System.out.println("Starting");
}
}
編譯這個文件,然后在當前文件位置創(chuàng)建一個文件META-INF/services/org.testng.ITestNGListener
這個名字就是你要實現的接口。
最后您應該知道如下的目錄結構,只有僅僅兩個文件:
$ tree
|____META-INF
| |____services
| | |____org.testng.ITestNGListener
|____test
| |____tmp
| | |____TmpSuiteListener.class
$ cat META-INF/services/org.testng.ITestNGListener
test.tmp.TmpSuiteListener
在這個目錄創(chuàng)建一個jar文件如下:
$ jar cvf ../sl.jar .
added manifest
ignoring entry META-INF/
adding: META-INF/services/(in = 0) (out= 0)(stored 0%)
adding: META-INF/services/org.testng.ITestNGListener(in = 26) (out= 28)(deflated -7%)
adding: test/(in = 0) (out= 0)(stored 0%)
adding: test/tmp/(in = 0) (out= 0)(stored 0%)
adding: test/tmp/TmpSuiteListener.class(in = 849) (out= 470)(deflated 44%)
接著,把你的jar文件放在當前你調用TestNG的classpath
上:
$ java -classpath sl.jar:testng.jar org.testng.TestNG testng-single.yaml
Starting
f2 11 2
PASSED: f2("2")
Finishing
這種機制允許你在同一組偵聽器適用于整個組織只需增加一個JAR文件添加到類路徑,而不是要求每一個開發(fā)者要記得在他們的testng.xml
文件中指定這些偵聽器。
5.18 - 依賴注入
TestNG的支持兩種不同類型的依賴注入:本機(由TestNG的本身執(zhí)行)和外部(通過依賴注入框架,如Guice)。
5.18.1 - 本地依賴注入
TestNG 允許你在自己的方法中聲明額外的參數。這時,TestNG會自動使用正確的值填充這些參數。依賴注入就使用在這種地方:
- 任何
@Before
或@Test
方法可以聲明一個類型為ITestContext
的參數。 - 任何
@After
都可以聲明一個類型為ITestResult
的單數,它代表了剛剛運行的測試方法。 - 任何
@Before
和@After
方法都能夠聲明類型為XmlTest
的參數,它包含了當前的<test>
參數。 - 任何
@BeforeMethod
可以聲明一個類型為java.lang.reflect.Method
的參數。這個參數會接收@BeforeMethod
完成調用的時候馬上就被調用的那個測試方法當做它的值。 - 任何
@BeforeMethod
可以聲明一個類型為Object[]
的參數。這個參數會包含要被填充到下一個測試方法中的參數的列表,它既可以又 TestNG 注入,例如java.lang.reflect.Method
或者來自@DataProvider
。 - 任何
@DataProvider
可以聲明一個類型為ITestContext
或java.lang.reflect.Method
的參數。后一種類型的參數,會收到即將調用的方法作為它的值。
你可以使用@NoInjection關閉注釋:
public class NoInjectionTest {
@DataProvider(name = "provider")
public Object[][] provide() throws Exception {
return new Object[][] { { CC.class.getMethod("f") } };
}
@Test(dataProvider = "provider")
public void withoutInjection(@NoInjection Method m) {
Assert.assertEquals(m.getName(), "f");
}
@Test(dataProvider = "provider")
public void withInjection(Method m) {
Assert.assertEquals(m.getName(), "withInjection");
}
}
5.18.2 - Guice依賴注入
如果您使用Guice,TestNG給你一個簡單的方法來與Guice模塊注入測試對象:
@Guice(modules = GuiceExampleModule.class)
public class GuiceTest extends SimpleBaseTest {
@Inject
ISingleton m_singleton;
@Test
public void singletonShouldWork() {
m_singleton.doSomething();
}
}
在這個例子中,GuiceExampleModule
被預期綁定到ISingleton
借口的一些具體的類:
public class GuiceExampleModule implements Module {
@Override
public void configure(Binder binder) {
binder.bind(ISingleton.class).to(ExampleSingleton.class).in(Singleton.class);
}
}
如果在指定的模塊實例化你的測試類需要更多的靈活性,您可以指定一個模塊工廠:
@Guice(moduleFactory = ModuleFactory.class)
public class GuiceModuleFactoryTest {
@Inject
ISingleton m_singleton;
@Test
public void singletonShouldWork() {
m_singleton.doSomething();
}
}
模塊工廠需要實現該IModuleFactory
借口:
public interface IModuleFactory {
/**
* @param context The current test context
* @param testClass The test class
*
* @return The Guice module that should be used to get an instance of this
* test class.
*/
Module createModule(ITestContext context, Class<?> testClass);
}
你的工廠將會通過了測試環(huán)境和TestNG需要實例化測試類的一個實例。你的createModule
方法應該返回一個Guice模塊,就會知道如何實例化這個測試類。您可以使用測試環(huán)境,以了解您的環(huán)境的詳細信息,如在規(guī)定的testng.xml
設置參數等...
5.19 - 監(jiān)聽方法調用
無論何時TestNG即將調用一個測試(被@Test
注解的)或者配置(任何使用@Before
or@After
注解標注的方法),監(jiān)聽器 IInvokedMethodListener
都可以讓你得到通知。你所要做的就是實現如下接口:
public interface IInvokedMethodListener extends ITestNGListener {
void beforeInvocation(IInvokedMethod method, ITestResult testResult);
void afterInvocation(IInvokedMethod method, ITestResult testResult);
}
并且就像在 關于TestNG監(jiān)聽器一節(jié) 中所討論的那樣,將其聲明為一個監(jiān)聽器。
5.20 - 重寫測試方法
TestNG的允許你覆蓋和跳過測試方法的調用。在那里,這是非常有用的一個例子,如果你需要你的測試方法需要一個特定的安全管理。通過提供實現IHookable
一個監(jiān)聽器,你就可以做到這一點。
下面是用JAAS(Java驗證和授權API)一個例子:
public class MyHook implements IHookable {
public void run(final IHookCallBack icb, ITestResult testResult) {
// Preferably initialized in a @Configuration method
mySubject = authenticateWithJAAs();
Subject.doAs(mySubject, new PrivilegedExceptionAction() {
public Object run() {
icb.callback(testResult);
}
};
}
}
6 - 測試結果
6.1 - 成功、失敗和斷言
如果一個測試沒有拋出任何異常就完成運行或者說拋出了期望的異常(參見@Test
注解的expectedExceptions
屬性文檔),就說,這個測試是成功的。
測試方法的組成常常包括拋出多個異常,或者包含各種各樣的斷言(使用Java "assert"
關鍵字)。一個"assert"
失敗會觸發(fā)一個AssertionErrorException
,結果就是測試方法被標記為失敗(記住,如果你看不到斷言錯誤,要在加上-ea
這個JVM參數)。
下面是個例子:
@Test
public void verifyLastName() {
assert "Beust".equals(m_lastName) : "Expected name Beust, for" + m_lastName;
}
TestNG 也包括 JUnit 的Assert
類,允許你對復雜的對象執(zhí)行斷言:
import static org.testng.AssertJUnit.*;
//...
@Test
public void verify() {
assertEquals("Beust", m_lastName);
}
注意,上述代碼使用了靜態(tài)導入,以便能夠使用assertEquals
方法,而無需加上它的類前綴。
6.2 - 日志與結果
當運行SuiteRunner
的時候會指定一個目錄,之后測試的結果都會保存在一個在那個目錄中叫做index.html
的文件中。這個index
文件指向其他多個HTML和文本文件,被指向的文件包含了整個測試的結果。你可以再 這里 看到一個例子。
通過使用監(jiān)聽器和報表器,可以很輕松的生成自己的TestNG報表:
- 監(jiān)聽器 實現接口
org.testng.ITestListener
,并且會在測試開始、通過、失敗等時刻實時通知 - 報告器 實現接口
org.testng.IReporter
,并且當整個測試運行完畢之后才會通知。IReporter
接受一個對象列表,這些對象描述整個測試運行的情況
例如,如果你想要生成一個PDF報告,那么就不需要實時通知,所以用IReporter
。如果需要寫一個實時報告,例如用在GUI上,還要在每次測試時(下面會有例子和解釋)有進度條或者文本報告顯示點 ("."),那么ITestListener
是你最好的選擇。
6.2.1 - 日志監(jiān)聽器
這里是對每個傳遞進來的測試顯示"."的監(jiān)聽器,如果測試失敗則顯示 "F" ,跳過則是"S":
public class DotTestListener extends TestListenerAdapter {
private int m_count = 0;
@Override
public void onTestFailure(ITestResult tr) {
log("F");
}
@Override
public void onTestSkipped(ITestResult tr) {
log("S");
}
@Override
public void onTestSuccess(ITestResult tr) {
log(".");
}
private void log(String string) {
System.out.print(string);
if (++m_count % 40 == 0) {
System.out.println("");
}
}
}
上例中,我們選擇擴展TestListenerAdapter
,它使用空方法實現了ITestListener
。所以我不需要去重寫那些我不需要的方法。如果喜歡可以直接實現接口。
這里是我使用這個新監(jiān)聽器調用TestNG的例子:
java -classpath testng.jar;%CLASSPATH% org.testng.TestNG -listener org.testng.reporters.DotTestListener test\testng.xml
輸出是:
........................................
........................................
........................................
........................................
........................................
.........................
===============================================
TestNG JDK 1.5
Total tests run: 226, Failures: 0, Skips: 0
===============================================
注意,當你使用-listener
的時候,TestNG 會自動的檢測你所使用的監(jiān)聽器類型。
6.2.2 - 日志報表
org.testng.IReporter
接口只有一個方法:
public void generateReport(List<ISuite> suites, String outputDirectory)
這個方法在 TestNG 中的所有測試都運行完畢之后被調用,這樣你可以方法這個方法的參數,并且通過它們獲得剛剛完成的測試的所有信息。
6.2.3 - JUnit 報表
TestNG 包含了一個可以讓TestNG的結果和輸出的XML能夠被JUnitReport
所使用的監(jiān)聽器。這里 有個例子,并且ant任務創(chuàng)建了這個報告:
<target name="reports">
<junitreport todir="test-report">
<fileset dir="test-output">
<include name="*/*.xml"/>
</fileset>
<report format="noframes" todir="test-report"/>
</junitreport>
</target>
6.2.4 - 報表 API
如果你要在HTML報告中顯示日志信息,那么就要用到類 org.testng.Reporter:Reporter.log("M3 WAS CALLED");


6.2.5 - XML 報表
TestNG 提供一種XML報表器,使得能夠捕捉到只適用于TestNG而不適用與JUnit報表的那些特定的信息。這在用戶的測試環(huán)境必須要是用TestNG特定信息的XML,而JUnit又不能夠提供這些信息的時候非常有用。下面就是這種報表器生成XML的一個例子:
<testng-results>
<suite name="Suite1">
<groups>
<group name="group1">
<method signature="com.test.TestOne.test2()" name="test2" class="com.test.TestOne"/>
<method signature="com.test.TestOne.test1()" name="test1" class="com.test.TestOne"/>
</group>
<group name="group2">
<method signature="com.test.TestOne.test2()" name="test2" class="com.test.TestOne"/>
</group>
</groups>
<test name="test1">
<class name="com.test.TestOne">
<test-method status="FAIL" signature="test1()" name="test1" duration-ms="0"
started-at="2007-05-28T12:14:37Z" description="someDescription2"
finished-at="2007-05-28T12:14:37Z">
<exception class="java.lang.AssertionError">
<short-stacktrace>java.lang.AssertionError
... Removed 22 stack frames
</short-stacktrace>
</exception>
</test-method>
<test-method status="PASS" signature="test2()" name="test2" duration-ms="0"
started-at="2007-05-28T12:14:37Z" description="someDescription1"
finished-at="2007-05-28T12:14:37Z">
</test-method>
<test-method status="PASS" signature="setUp()" name="setUp" is-config="true" duration-ms="15"
started-at="2007-05-28T12:14:37Z" finished-at="2007-05-28T12:14:37Z">
</test-method>
</class>
</test>
</suite>
</testng-results>
這個報表器是隨著默認監(jiān)聽器一起諸如的,所以你默認的情況下就可以得到這種類型的輸出。這個監(jiān)聽器提供了一些屬性,可以修改報表來滿足你的需要。下表包含了這些屬性,并做簡要說明:
屬性 | 說明 | Default value |
---|---|---|
outputDirectory | 一個String 指明了XML文件要存放的目錄 |
TestNG 輸出目錄 |
timestampFormat | 指定報表器生成日期字段的格式 | yyyy-MM-dd'T'HH:mm:ss'Z' |
fileFragmentationLevel | 值為1,2或3的整數,指定了XML文件的生成方式:1 - 在一個文件里面生成所有的結果。2 - 每套測試都單獨生成一個XML文件,這些文件被鏈接到一個主文件。3 - 同2,不過添加了引用那些套測試的測試用例的XML文件。 | 1 |
splitClassAndPackageNames |
boolean 值,指明了對<class> 元素的生成方式。例如,你在false 的時候得到<class class="com.test.MyTest"> ,在true的 時候<class class="MyTest" package="com.test"> 。 |
false |
generateGroupsAttribute |
boolean 值指定了對<test-method> 元素是否生成groups 屬性。這個功能的目的是,對那些無需遍歷整個<group> 元素,且只含有一個測試方法的組來說,可以提供一種更直接的解析組的方式。 |
false |
stackTraceOutputMethod | 指定在發(fā)生異常的時候生成異常,追蹤彈棧信息的類型,有如下可選值:0 - 無彈棧信息 (只有異常類和消息)。1 - 尖端的彈棧信息,從上到下只有幾行。2 - 帶有所有內部異常的完整的彈棧方式。3 - 短長兩種彈棧方式 | 2 |
generateDependsOnMethods | 對于<test-method> 元素,使用這個屬性來開啟/關閉 depends-on-methods 屬性。 |
true |
generateDependsOnGroups | 對于<test-method> 屬性開啟depends-on-groups 屬性。 |
true |
為了配置報表器,你可以在命令行下使用-reporter
選項,或者在 Ant 任務中嵌入<reporter>
元素。對于每個情況,你都必須指明類org.testng.reporters.XMLReporter
。但是要注意你不能夠配置內建的報表器,因為這個只使用默認配置。如果你的確需要XML報表,并且使用自定義配置,那么你就不得不手工完成。可以通過自己添加一兩個方法,并且禁用默認監(jiān)聽器達到目的。