TestNG測試用例重跑詳解及實踐最佳化
測試用例執行穩定性是自動化質量的一個重要指標,在執行中需要儘可能的剔除非bug造成的測試用例執行失敗,對於失敗用例進行重跑是常用策略之一。一種重跑策略是所有用例執行結束後對失敗用例重跑,另一種重跑策略是在執行時監控用例執行狀態,失敗後實時重跑。
下面,詳細介紹TestNG如何對失敗測試用例實時重跑並解決重跑過程中所遇到問題的實踐和解決方案。對失敗測試用例進行實時重跑,有以下幾個方面需求:
- 測試用例執行失敗,監聽到失敗後立即進行重跑
- 測試用例透過
dependsOnMethods/dependsOnGroups
標記依賴其他測試用例,在被依賴的測試用例重跑執行成功後,該測試用例可以繼續執行 - 對於重跑多次的測試用例,只記錄最後一次執行成功或失敗結果
第一部分 測試用例重跑
1.1 retryAnalyzer註解方式
對於希望測試用例中的少量易失敗,不穩定的測試用例進行重跑,可採用這種方式。
1.1.1 原理
以下是TestNG處理測試用例執行結果的部分程式碼。
IRetryAnalyzer retryAnalyzer = testMethod.getRetryAnalyzer(); boolean willRetry = retryAnalyzer != null && status == ITestResult.FAILURE && failure.instances != null && retryAnalyzer.retry(testResult); if (willRetry) { resultsToRetry.add(testResult); failure.count++; failure.instances.add(testResult.getInstance()); testResult.setStatus(ITestResult.SKIP); } else { testResult.setStatus(status); if (status == ITestResult.FAILURE && !handled) { handleException(ite, testMethod, testResult, failure.count++); }
分析以上程式碼,其中,介面
IretryAnalyzer
的方法
retry()
的返回值作為是否對失敗測試用例進行重跑的一個條件。如果
retry()
結果為
true
,則該失敗測試用例會重跑,同時將本次失敗結果修改為
Skip
;如果結果為
false
,則失敗的測試用例保持失敗結果,執行結束。因此,如果你希望失敗測試用例重跑的話,需要把
IretryAnalyzer的retry()
方法重寫,插入自己定義的邏輯,設定返回值為
true
。
1.1.2 程式碼
建立類
RetryImpl
,重寫
retry()
方法,設定失敗測試用例的重跑次數,程式碼如下,:
public class RetryImpl implements IRetryAnalyzer { private int count = 1; private int max_count = 3; // Failed test cases could be run 3 times at most @Override public boolean retry(ITestResult result) { System.out.println("Test case :"+result.getName()+",retry time: "+count+""); if (count < max_count) { count++; return true; } return false; } }
1.1.3 例項
public class TestNGReRunDemo { @Test(retryAnalyzer=RetryImpl.class) public void test01(){ Assert.assertEquals("success","fail"); System.out.println("test01"); } }
以上測試用例test01可重複執行3次。
1.2 實現介面IAnnotationTransformer方法
如果希望所有失敗的測試用例都進行重跑,採用
retryAnalyzer
註解方式對每個測試用例進行註解就比較麻煩。透過實現
IAnnotationTransformer
介面的方式,可以對全量測試用例的重試類進行設定。 該介面是一個監聽器介面,用來修改TestNG註解。
IAnnotationTransformer
監聽器介面只有一個方法:
transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod)
. 上文中,我們自定義了類
RetryImpl
實現介面
IRetryAnalyzer
。TestNG透過
transfrom()
方法修改
retryAnalyzer
註解。以下程式碼對
retryAnalyzer
註解進行修改設定。
1.2.1程式碼
建立類
RetryListener
,程式碼如下。
public class RetryListener implements IAnnotationTransformer { public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) { IRetryAnalyzer retry = annotation.getRetryAnalyzer(); if (retry == null) { annotation.setRetryAnalyzer(RetryImpl.class); } } }
1.2.2 配置Listener
TestNG可以在配置檔案或者測試類中對
Listener
類進行配置。
- 方法一: 在TestNG的配置XML中進行以下配置
<listeners> <listener class-name="PackageName.RetryListener"></listener> </listeners>
- 方法二: 在測試類中透過@Listeners配置
@Listeners({RetryListener.class}) public class TestNGReRunDemo { @Test public void test01(){ Assert.assertEquals("success","fail"); System.out.println("test01"); } }
配置完成後,執行測試用例test01,執行結果顯示test01將重跑次數3次。
第二部分 被依賴的測試用例重跑結果處理
進一步分析TestNG的執行程式碼,其在對失敗執行用例重跑時,邏輯如下圖。
對於透過
dependsOnMethods
或
dependsOnGroups
註解依賴於其他測試用例的測試用例來講,測試用例執行分為兩種情況:
- alwaysRun=true,則無論所依賴的測試用例執行情況如何,該測試用例都會執行,即所依賴的測試用例重跑不會影響該測試用例的執行。
- alwaysRun=false,或者保持預設值(false),依賴於其他測試用例或測試用例組的測試結果,在執行時TestNG獲取所依賴的測試用例的執行結果,檢查依賴的測試用例是否全部執行成功,如果不全部成功,則把該測試用例結果設定為Skipped。
2.1 場景分析:場景一
被依賴的測試用例失敗後進行了重跑,並重跑成功。(注:在
RetryImpl
類中,
已設定最大重跑次數max_count = 3)
public static int number =0; @Test public void test01(){ number++; System.out.println(String.valueOf(number)); Assert.assertEquals(number,2); System.out.println("test01"); } @Test(dependsOnMethods = "test01") // alwaysRun = false by default public void test02(){ System.out.println("test02 is running only if test01 is passed."); }
1、TestNG測試報告
2、問題
測試用例 | 執行次數 | 執行情況 | 測試報告 |
---|---|---|---|
Test01 | 2 | 第一次:skipped ; 第二次:passed | 在Skipped 和Passed的統計數量中,test01被分別記錄一次 |
Test02 | 0 | Skipped | 記錄一次Skipped |
-
測試報告:test01執行結果全部被記錄,而用例重跑,只希望記錄最後的結果。
-
執行情況:測試用例test02依賴於測試用例test01執行結果,在test01重跑成功後,測試用例test02沒有執行,不符合需求預期。
2.2 場景分析:場景二
被依賴的測試用例失敗後進行了重跑,並且重跑沒有成功。(注:在RetryImpl類中,已設定最大重跑次數max_count = 3)
public static int number =0; @Test public void test01(){ number++; System.out.println(String.valueOf(number)); Assert.assertEquals(number,10); System.out.println("test01"); } @Test(dependsOnMethods = "test01") // alwaysRun = false by default public void test02(){ System.out.println("test02 is running only if test01 is passed."); }
1、TestNG測試報告
2、問題
測試用例 | 執行次數 | 執行結果 | 測試報告 |
---|---|---|---|
Test01 | 3 | 第一次:skipped;第二次:skipped;第三次:failed | 在Skipped統計數量中,test01被被記錄兩次在failed統計中,test01被記錄一次 |
Test02 | 0 | Skipped | 記錄一次Skipped |
- 執行情況:測試用例test02依賴於測試用例test01執行結果,在test01重跑失敗後,測試用例test02沒有執行,這種情況符合需求預期。
- 測試報告:同場景一,test01重跑失敗,執行結果全部被記錄,而用例重跑,只希望記錄最後的結果。
第三部分 最佳化解決方案
以下方案解決重跑測試用例成功後後繼測試用例無法繼續執行的問題,並對測試報告進行最佳化。
3.1
TestListenerAdapter
方法重寫
根據上面分析的TestNG邏輯,在對依賴測試用例的結果進行檢查時,如果忽略重跑的中間結果只檢查最後一次的執行結果,可以達到需求的目的。對於測試報告,同樣的處理方式,忽略所有中間的測試用例執行結果,只記錄最後結果。 測試用例的中間執行結果為
Skipped
,下面的程式碼透過重寫
TestListenerAdapter
的
onTestSuccess()
和
onTestFailure()
方法,對測試用例的中間結果
skipped
進行了刪除。程式碼如下:
public class ResultListener extends TestListenerAdapter { @Override public void onTestFailure(ITestResult tr) { if(tr.getMethod().getCurrentInvocationCount()==1) { super.onTestFailure(tr); return; } processSkipResult(tr); super.onTestFailure(tr); } @Override public void onTestSuccess(ITestResult tr) { if(tr.getMethod().getCurrentInvocationCount()==1) { super.onTestSuccess(tr); return; } processSkipResult(tr); super.onTestSuccess(tr); } // Remove all the dup Skipped results public void processSkipResult(ITestResult tr) { ITestContext iTestContext = tr.getTestContext(); Iterator<ITestResult> processResults = iTestContext.getSkippedTests().getAllResults().iterator(); while (processResults.hasNext()) { ITestResult skippedTest = (ITestResult) processResults.next(); if (skippedTest.getMethod().getMethodName().equalsIgnoreCase(tr.getMethod().getMethodName()) ) { processResults.remove(); } } } }
3.2 配置結果處理Listener類
在配置檔案進行全域性設定或者在測試類中標記。
- 方法一:在TestNG的配置XML中進行以下配置
<listeners> <listener class-name="PackageName.ResultListener"></listener> </listeners>
- 方法二:在測試類中透過@Listeners配置
@Listeners({ResultListener.class}) public class TestNGReRunDemo { @Test public void test01(){ Assert.assertEquals("success","fail"); System.out.println("test01"); } }
3.3 場景一
1、 結果驗證
2、 結果分析:
測試用例 | 執行次數 | 執行結果 | 測試報告 |
---|---|---|---|
Test01 | 2 | 第一次:skipped;第二次:passed | 只在Passed的統計數量中test01被記錄一次 |
Test02 | 1 | Passed | 記錄一次passed |
3.4 場景二
1、 結果驗證
2、 結果分析:
測試用例 | 執行次數 | 執行結果 | 測試報告 |
---|---|---|---|
Test01 | 3 | 第一次:skipped;第二次:skipped;第三次:failed | test01只在failed統計中被記錄一次 |
Test02 | 1 | Skipped | 依賴用例執行失敗,test02結果為Skipped,只記錄一次結果Skipped |
作者:耿燕飛
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69918724/viewspace-2690039/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- TestNG測試框架之失敗測試重跑框架
- testng擴充套件 失敗的用例重跑套件
- RocketMQ 重試機制詳解及最佳實踐MQ
- 測試用例最佳實踐
- 如何重試 TestNg 中引數化的單條用例?
- TestNg 失敗用例重試自動被 ignored 怎麼辦?
- TestNg失敗重跑—解決使用 dataProvider 引數化用例次數衝突問題IDE
- 針對 “測試用例最佳實踐” 的說明
- DDS協議解讀及測試開發實踐協議
- 最詳細的測試用例設計方法講解
- TDD及單元測試最佳實踐
- 測試面試-測試用例面試
- 研效最佳化實踐:WeTest提效測試
- 自動化測試實戰技巧:「用例失敗重試機制」實現方案分享
- 測試用例
- 單元測試工具 TestNG 使用
- Eolink Apikit 圖形用例詳解|API 自動化測試API
- TestNg之XMl形式實現多執行緒測試XML執行緒
- 使用TypeScript改造構建工具及測試用例TypeScript
- 【5】測試用例
- 【pytest官方文件】解讀Skipping test functions,跳過測試用例詳解Function
- Oracle minus用法詳解及應用例項Oracle
- 約跑APP測試APP
- 不經常用到但又非常重要的測試用例編寫方法——測試大綱法詳解
- 手工測試用例與自動化測試用例的區別
- ab壓力測試命令及引數詳解
- 使用 testng 做介面自動化測試
- Spring、Spring Boot和TestNG測試指南 – 共享測試配置Spring Boot
- postman寫測試用例Postman
- IOS測試用例例子iOS
- 測試用例的方法
- 黑盒測試用例二
- 面經-測試用例
- 測試環境使用問題及其最佳化對策實踐
- Rest Assured+TestNg實現資料驅動的介面測試REST
- 【自動化基礎】allure描述用例詳細講解及實戰
- Cypress系列(90)- Cypress.Cookies 命令詳解以及如何跨測試用例共享 CookiesCookie
- MQTT協議詳解及v5.0實踐MQQT協議