TestNG測試用例重跑詳解及實踐優化

測試用例運行穩定性是自動化質量的一個重要指標,在運行中須要儘量的剔除非bug形成的測試用例執行失敗,對於失敗用例進行重跑是經常使用策略之一。一種重跑策略是全部用例運行結束後對失敗用例重跑,另外一種重跑策略是在運行時監控用例運行狀態,失敗後實時重跑。

下面,詳細介紹TestNG如何對失敗測試用例實時重跑並解決重跑過程當中所遇到問題的實踐和解決方案。對失敗測試用例進行實時重跑,有如下幾個方面需求:ide

  1. 測試用例運行失敗,監聽到失敗後當即進行重跑
  2. 測試用例經過dependsOnMethods/dependsOnGroups標記依賴其餘測試用例,在被依賴的測試用例重跑運行成功後,該測試用例能夠繼續運行
  3. 對於重跑屢次的測試用例,只記錄最後一次運行成功或失敗結果

第一部分 測試用例重跑

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()方法重寫,插入本身定義的邏輯,設置返回值爲truespa

1.1.2 代碼

建立類RetryImpl,重寫retry()方法,設置失敗測試用例的重跑次數,代碼以下,:code

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次。orm

1.2 實現接口IAnnotationTransformer方法

若是但願全部失敗的測試用例都進行重跑,採用retryAnalyzer註解方式對每一個測試用例進行註解就比較麻煩。經過實現IAnnotationTransformer接口的方式,能夠對全量測試用例的重試類進行設置。
該接口是一個監聽器接口,用來修改TestNG註解。IAnnotationTransformer監聽器接口只有一個方法:transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod). 上文中,咱們自定義了類RetryImpl 實現接口IRetryAnalyzer。TestNG經過transfrom()方法修改retryAnalyzer註解。如下代碼對retryAnalyzer註解進行修改設置。blog

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類進行配置。ip

  • 方法一:在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次。rem

第二部分 被依賴的測試用例重跑結果處理

進一步分析TestNG的運行代碼,其在對失敗運行用例重跑時,邏輯以下圖。

對於經過dependsOnMethodsdependsOnGroups註解依賴於其餘測試用例的測試用例來說,測試用例執行分爲兩種狀況:

  • 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.");
}

一、TestNG測試報告

二、問題

測試用例 運行次數 運行狀況 測試報告
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.");
}

一、TestNG測試報告

二、問題

測試用例 運行次數 運行結果 測試報告
Test01 3 第一次:skipped;第二次:skipped;第三次:failed 在Skipped統計數量中,test01被被記錄兩次在failed統計中,test01被記錄一次
Test02 0 Skipped 記錄一次Skipped
  • 運行狀況:測試用例test02依賴於測試用例test01運行結果,在test01重跑失敗後,測試用例test02沒有執行,這種狀況符合需求預期。
  • 測試報告:同場景一,test01重跑失敗,運行結果所有被記錄,而用例重跑,只但願記錄最後的結果。

第三部分 優化解決方案

如下方案解決重跑測試用例成功後後繼測試用例沒法繼續運行的問題,並對測試報告進行優化。

3.1 TestListenerAdapter方法重寫

根據上面分析的TestNG邏輯,在對依賴測試用例的結果進行檢查時,若是忽略重跑的中間結果只檢查最後一次的運行結果,能夠達到需求的目的。對於測試報告,一樣的處理方式,忽略全部中間的測試用例運行結果,只記錄最後結果。
測試用例的中間運行結果爲Skipped,下面的代碼經過重寫TestListenerAdapteronTestSuccess()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 場景一

一、 結果驗證

二、結果分析:

測試用例 運行次數 運行結果 測試報告
Test01 2 第一次:skipped;第二次:passed 只在Passed的統計數量中test01被記錄一次
Test02 1 Passed 記錄一次passed

3.4 場景二

一、結果驗證

二、結果分析:

測試用例 運行次數 運行結果 測試報告
Test01 3 第一次:skipped;第二次:skipped;第三次:failed test01只在failed統計中被記錄一次
Test02 1 Skipped 依賴用例執行失敗,test02結果爲Skipped,只記錄一次結果Skipped
做者:耿燕飛 來源:宜信技術學院
相關文章
相關標籤/搜索