TestNG 監聽器 ITestListener


https://www.ibm.com/developerworks/cn/opensource/os-cn-testinglistener/

實戰 TestNG 監聽器

TestNG 是一款被廣泛使用的自動化測試框架。TestNG 監聽器是 TestNG 中的一些接口,通這些接口能夠為 TestNG 提供定制化的功能。本文是一篇指導讀者如何使用 TestNG 監聽器的文章。文章還對 TestNG 的幾種監聽器進行了一一介紹,之后通過代碼實例向讀者展示了如何使用部分 TestNG 監聽器。

沈 銳, 軟件工程師, IBM

2015 年 1 月 26 日

  • expand內容

TestNG 簡介

TestNG 是一個開源的自動化測試框架,其靈感來自 JUnit 和 NUnit,但它引入了一些新功能,使其功能更強大,更易於使用。TestNG 的設計目標是能夠被用於進行各種類型測試:單元測試、功能測試,端到端測試、集成測試,等等。NG 是 Next Generation 的簡寫,表示下一代,意在表示其產生的目的是要超越當前所有測試框架。TestNG 類似於 JUnit(特別是 JUnit 4),但它不是 JUnit 的擴展,而是獨立的全新設計的框架。TestNG 的創造者是 Cedric Beust(塞德里克·博伊斯特)。

TestNG 有如下特點:

  • 支持 Java 注釋功能
  • 測試運行在任意大的線程池中,並且有多種運行策略可供選擇(所有測試方法運行在自己的線程中、每個測試類一個線程,等等)。
  • 線程安全
  • 靈活的測試配置
  • 支持數據驅動測試(通過 @DataProvider 注釋)
  • 支持參數化
  • 強大的運行模型(不再使用 TestSuite)
  • 有多種工具和插件支持(Eclipse, IDEA, Maven, 等等)
  • 內嵌 BeanShell 以進一步增強靈活性
  • 默認提供 JDK 的運行時和日志功能
  • 提供應用服務器測試依賴的方法
 

TestNG 監聽器概述

盡管 TestNG 的默認配置已經提供了不少強大的功能和靈活的選項,但是沒有一種方案能夠解決所有的問題。在實際應用中,我們多多少少會發現 TestNG 自帶的功能無法滿足我們的一些實際需求,尤其是關於測試方法運行的行為、報表以及通知功能。此時,我們就需要使用 TestNG 的監聽器定制額外的功能以滿足我們的需要。

以下是 TestNG 提供的幾種監聽器:

  • IAnnotationTransformer
  • IAnnotationTransformer2
  • IHookable
  • IInvokedMethodListener
  • IMethodInterceptor
  • IReporter
  • ISuiteListener
  • ITestListener

盡管名字叫監聽器,但事實上它們只是一些預定義的 Java 接口。用戶創建這些接口的實現類,並把它們加入到 TestNG 中,TestNG 便會在測試運行的不同時刻調用這些類中的接口方法。接下來,我們一一介紹 TestNG 中的每種監聽器。

IAnnotationTransformer

大多數情況下,在運行時我們不需要改動源代碼中定義的注釋,但有時需要這樣做。這時,我們就需要使用 IAnnotationTransformer 監聽器。IAnnotationTransformer 只能用來修改 @Test 注釋,如果需要修改其他 TestNG 的注釋(比如,@DataProvider, @Factory 以及 @Configuration),需要使用 IAnnotationTransformer2 監聽器。IAnnotationTransformer 要求實現 transform 方法,其方法簽名如下:

void transform(ITest annotation, Class testClass, Constructor testConstructor, Method testMethod);

annotation 代表就是為 testMethod 定義的 @Test 注釋。調用其方法可以更改 @Test 注釋屬性。例如,下面的代碼在運行時將屬性 enabled 改為 false 從而禁用了當前的測試方法。

annotation.setEnabled(false);

IAnnotationTransformer2

前文已提到 IAnnotationTransformer2 監聽器是用來在運行時修改除 @Test 以外的 TestNG 的注釋。下面是該監聽器要求實現的方法。

void transform(IConfigurationAnnotation annotation, java.lang.Class testClass, 
 java.lang.reflect.Constructor testConstructor,
 java.lang.reflect.Method testMethod)
void transform(IDataProviderAnnotation annotation, java.lang.reflect.Method method)
void transform(IFactoryAnnotation annotation, java.lang.reflect.Method method)

可見,目前只有 @Configuration,@DataProvider 以及 @Factory 注釋能夠通過該監聽器修改。而事實上,@Configuration 在最新版本中已不被推薦使用,需用 @BeforeSuite,@AfterSuite 等注釋替代。

IHookable

IHookable 監聽器提供了類似與面向方面編程(AOP)中的 Around Advice 的功能。它在測試方法執行前后提供了切入點,從而使用戶能夠在測試方法運行前后注入特定的功能。例如,用戶可以在當前測試方法運行前加入特定的驗證邏輯以決定測試方法是否運行或者跳過,甚至覆蓋測試方法的邏輯。下面是 IHookable 監聽器要求實現的方法簽名。

void run(IHookCallBack callBack, ITestResult testResult)

如要運行原始測試方法邏輯,需要調用 runTestMethod 方法。

callBack.runTestMethod(testResult);

IInvokedMethodListener

與 IHookable 類似,IInvokedMethodListener 提供了類似與面向方面編程(AOP)中的 Before Advice 和 After Advice 的功能。它允許用戶在當前測試方法被執行前和執行后注入特定的邏輯,比如,可以加入日志方法。用戶需要實現的方法如下。

void afterInvocation(IInvokedMethod method, ITestResult testResult) 
void beforeInvocation(IInvokedMethod method, ITestResult testResult)

IMethodInterceptor

TestNG 啟動之后,第一件要做的事情是將所有的測試方法分成兩類:一類是順序運行的測試方法;一類是沒有特定運行順序的測試方法。

TestNG 通過 @Test 注釋中的 dependsOnGroups 和 dependsOnMethods 使用戶能夠定義測試方法之間的依賴關系。這種依賴關系也就決定這些測試方法必須按着怎樣的順序運行,這就是第一類。除此以外的便是第二類。對於第二類中的測試方法,盡管默認 TestNG 會嘗試用類名將它們分組,但是理論上,它們的運行順序是隨機的,甚至每次運行的順序都可能不同。因此為了使用戶擁有對第二類測試方法更大的控制權,IMethodInterceptor 監聽器產生了。用戶要實現的方法如下。

java.util.List<IMethodInstance> intercept(java.util.List<IMethodInstance> methods, ITestContext context)

intercept 方法在所有測試方法被分類后以及所有測試方法被執行前被調用。所有的測試方法將按照 intercept 返回值列表中的順序被執行。因此,用戶在 intercept 方法中可以對列表進行修改,比如重新排序,甚至增加或者減少測試方法。

IReporter

TestNG 提供了默認的測試報表。但如果用戶希望有不同格式的測試報表,就需要使用 IReporter 監聽器。IReporter 監聽器只有一個方法需要實現。

void generateReport(java.util.List<XmlSuite> xmlSuites, java.util.List
<ISuite> suites, java.lang.String outputDirectory)

該方法在所有測試方法執行結束后被調用,通過遍歷 xmlSuites 和 suites 能夠獲取所有測試方法的信息以及測試結果。outputDirectory 是默認的測試報表生成路徑,當然你可以指定其他路徑生成報表。

ISuiteListener

ISuiteListener 類似於 IInvokedMethodListener,區別是 IInvokedMethodListener 針對的是測試方法,而 ISuiteListener 針對的是測試套件。ISuiteListener 使用戶有機會在測試套件開始執行以及執行結束之后嵌入自己的邏輯。該監聽器要求實現的方法如下。

void onFinish(ISuite suite) 
void onStart(ISuite suite)

ITestListener

如果要在測試方法執行成功、失敗或者跳過時指定不同后續行為,可以通過 IInvokedMethodListener 實現,不過更為簡便的方式是利用 ITestListener 監聽器。ITestListener 監聽器要求實現的方法中包含如下三個。

void onTestFailure(ITestResult result) 
void onTestSkipped(ITestResult result) 
void onTestSuccess(ITestResult result)

除了以上三個方法,ITestListener 還聲明了其他一些方法,大家可以自行查閱 TestNG Javadoc 了解細節。

另外,TestListenerAdapter 已經實現 ITestListener,並且提供了一些有用的方法,比如分別獲取所有成功失敗跳過三種測試結果的測試方法的方法,並且 ITestListner 中有很多方法而 TestListenerAdapter 已給出了默認實現。因此,繼承 TestListenerAdapter 后,便只需關注需要修改的方法。

 

監聽器的使用方法

前文已講過,監聽器的編碼過程就是定義一個 Java 類實現監聽器接口。下面簡單介紹一下監聽器的幾種使用方法。

在 testng.xml 中使用 TestNG 監聽器

TestNG 通過 testng.xml 配置所有的測試方法。Testng.xml 提供了 listeners 和 listener 標簽用來添加自定義的監聽器。下面示范的是本文示例代碼中包含的 testng.xml 文件。

<suite name="TestNGSample">
	<listeners>
		<listener class-name="listeners.OSFilter" />
		<listener class-name="listeners.ProgressTracker" />
	</listeners>
	<test name="ProgressTracker Demo">
		<classes>
			<class name="tests.SampleTest" />
		</classes>
	</test>
</suite>

在源代碼中使用 TestNG 監聽器

通過 @Listeners 注釋,可以直接在 Java 源代碼中添加 TestNG 監聽器。下面示范的是本文示例代碼中如何使用 @Listeners 注釋。

@Listeners({ OSFilter.class, ProgressTracker.class })
public class SampleTest {

	@Test(groups = { OSNames.OS_LINUX })
	public void test1() {
		sleep(5000);
		System.out.println(">>>test1");
	}

值得注意的是:

  • 在 @Listeners 中添加監聽器跟在 testng.xml 添加監聽器一樣,將被應用到整個測試套件中的測試方法。如果需要控制監聽器的應用范圍(比如添加的監聽器僅使用於某些測試測試類或者某些測試方法),則必須在監聽器類中編寫適當的判斷邏輯。
  • 在 @Listeners 中添加監聽器跟在 testng.xml 添加監聽器的不同之處在於,它不能添加 IAnnotationTransformer 和 IAnnotationTransformer2 監聽器。原因是因為這兩種監聽器必須在更早的階段添加到 TestNG 中才能實施修改注釋的操作,所以它們只能在 testng.xml 添加。
  • TestNG 對添加的監聽器不做去重判斷。因此,如果 testng.xml 和源代碼中添加了相同的監聽器,該監聽器的方法會被調用兩次。有關這一點,大家可以通過運行本文附帶的示例代碼包中 testng.xml 驗證。因此,切記,不要通過多種方式重復添加監聽器。

通過 ServiceLoader 使用 TestNG 監聽器

Java SE 6 開始提供了 ServiceLoader。它可以幫助用戶查找、加載和使用服務提供程序,從而在無需修改原有代碼的情況下輕易地擴展目標應用程序。通過 ServiceLoader 的方式使用 TestNG 監聽器,簡單來說,就是創建一個 jar 文件,里面包含 TestNG 監聽器的實現類已經 ServiceLoader 需要的配置信息,並在運行 TestNG 時把該 jar 文件加載到類路徑中。具體步驟請查閱 TestNG 官方文檔。這樣做的好處是:

  1. 可以輕松地與其他人分享 TestNG 監聽器。
  2. 當有很多 testng.xml 文件時,不需要重復把監聽器添加到每個文件中。

通過命令行使用 TestNG 監聽器

通過命令行使用 TestNG 監聽器,需要在命令行中加入”-listener”參數。如要指定多個監聽器,用逗號分隔。下面是一個調用的示例。

java org.testng.TestNG -listener MyListener testng1.xml [testng2.xml testng3.xml ...]

通過 IDE 使用 TestNG 監聽器

TestNG 在多種 IDE 中都有插件支持,比如 Eclipse 和 IDEA。因為最終 IDE 也是以命令行的方式調用 TestNG,因此在 IDE 中也是通過添加“-listener”參數使用 TestNG 監聽器。下圖以 Eclipse 為例示范了 TestNG 監聽器的配置方法。

圖 1. Eclipse 中 TestNG 監聽器的配置
圖 1. Eclipse 中 TestNG 監聽器的配置

除此之外,ANT 跟 Maven 也都有相應 Task 和插件運行 TestNG 測試,按照相應的文檔配置監聽器即可,這里便不一一贅述。

 

示例

本文提供了兩個示范的監聽器的實現,它們分別實現了動態測試方法過濾和測試進度跟蹤功能。

動態測試方法過濾監聽器(listeners.OSFilter)

TestNG 提供了分組特性,但它的局限是組名必須是靜態的。假如,現在有一些測試方法,部分適用於 Linux 和 Windows,部分僅適用於其一。通過默認的 TestNG 分組特性,大概要定義兩個 testng.xml 文件,指定不同的組名,並且在指定測試應用時要小心不要把配置與環境的對應弄錯。

示例代碼中的監聽器采用的方法是在每個測試方法執行前,動態獲取操作系統類型信息並將其與 @Test 注釋中定義的操作系統比較以決定哪些測試方法應該運行。這樣便省卻了上述配置的麻煩。

如果僅僅為了跳過不合適的測試方法,也可以選用 IInvokedMethodListener 監聽器。但因為該監聽器還要統計所有傳入的測試方法以及被忽略的測試方法的數目,所以選用了 IMethodInterceptor。值得注意的是,在 TestNG 的生命周期中,IMethodInterceptor 監聽器的 intercept 方法事實上會被調用兩次。為了避免代碼被重復執行,本示例代碼將返回的測試方法列表定義為成員變量,並通過判斷該成員變量是否為 null 決定是否執行過濾邏輯。

測試進度跟蹤監聽器(listeners.ProgressTracker)

自動化單元測試和 API 測試通常運行比較快,但是 UI 測試運行較慢。對於長時間運行的測試,我們常常想要知道當前正在運行的測試方法名稱以及預計剩余執行時間。這便是該監聽器實現的功能。

由於需要通過統計每個測試方法的運行時間來預估剩余執行時間,該監聽器選用了 IInvokedMethodListener。另外,預估剩余執行時間還需要知道整個測試套件要執行的測試方法的數目,單單使用 IInvokedMethodListener 無法得到該信息,因此,該監聽器通過 listeners.OSFilter 計算全部要執行的測試方法的數目。預估的算法是根據已經使用的時間和執行的測試方法數量計算出每個測試方法的平均執行時間,然后用該平均時間乘以未執行的測試方法數目,從而得出預估剩余時間。該算法的問題在於,當每個測試方法執行時間差異較大並且測試方法數目較少時,該方法春在較大的誤差,因此該時間只能作為參考。

如何運行示例代碼

示例代碼是一個 Eclipse 項目導出的壓縮文件,因此只要在 Eclipse 中導入該文件並安裝 TestNG 的 Eclipse 插件即可運行。

tests.SampleTest 是一個示范的 TestNG 測試類,該類中定義了 5 個測試方法:一個指定為僅運行在 Linux,兩個指定為運行在 Linux 和 Windows,另外兩個指定為僅運行在 Windows。為簡單期間,測試方法體僅用 Thread.sleep()模擬。該類中已添加了 @Listeners 注釋,將該類作為 TestNG 測試運行,將得到如下的正常輸出(此為 Linux 下的輸出結果)。

[TestNG] Running:
 /tmp/testng-eclipse-814456884/testng-customsuite.xml

Ignored Test Methods: [test4(tests.SampleTest), test5(tests.SampleTest)]
[Begin] test1(SampleTest) 
>>>test1
[Progress]33% (1/3) , Elapsed:00:00:05, Estimated Time Remaining:00:00:10
[End] test1(SampleTest): Passed

[Begin] test2(SampleTest) 
>>>test2
[Progress]67% (2/3) , Elapsed:00:00:08, Estimated Time Remaining:00:00:04
[End] test2(SampleTest): Passed

[Begin] test3(SampleTest) 
>>>test3
[Progress]100% (3/3) , Elapsed:00:00:10, Estimated Time Remaining:00:00:00
[End] test3(SampleTest): Passed

PASSED: test1
PASSED: test2
PASSED: test3

===============================================
 Default test
 Tests run: 3, Failures: 0, Skips: 0
===============================================


===============================================
Default suite
Total tests run: 3, Failures: 0, Skips: 0
===============================================

[TestNG] Time taken by org.testng.reporters.JUnitReportReporter@5a31442c: 12 ms
[TestNG] Time taken by org.testng.reporters.EmailableReporter@64a0401: 5 ms
[TestNG] Time taken by org.testng.reporters.XMLReporter@a98e9456: 20 ms
[TestNG] Time taken by org.testng.reporters.SuiteHTMLReporter@276fc2ae: 77 ms
[TestNG] Time taken by [FailedReporter passed=0 failed=0 skipped=0]: 1 ms
[TestNG] Time taken by org.testng.reporters.jq.Main@6517ab3c: 63 ms

示例代碼包中還包含有一個 testng.xml 文件。testng.xml 中也添加了監聽器,因此運行 testng.xml 將看到重復的測試進度信息輸出。需刪除 tests.SampleTest 中 @Listeners 注釋才能使 testng.xml 正常運行測試。

 

結束語

通過本文的介紹,大家可以了解到,TestNG 提供的多種監聽器接口使 TestNG 具備強大的擴展性。選用什么監聽器接口需根據實際需求而定。在動手開發自己的 TestNG 監聽器之前,不妨先搜索一下互聯網,興許其他用戶已經開發了類似的功能。例如,ReportNG 就是一款較為成熟的用於增強 TestNG 報表功能的插件。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM