在使用 testng 執行 UI 自動化用例時,因爲 UI自動化的不穩定性,咱們在測試的時候,每每會加上失敗重跑機制。在不使用 @DataProvider 提供用例參數化時,是不會有什麼問題,若是使用了的話就會出現多條用例都是失敗時,重跑機制只會執行第一次失敗的用例,其餘用例的失敗重跑就不執行了。java
以下:提供的兩組參數都是失敗時!(重跑的次數設置爲2次)ide
從上圖中能夠看出,第一次失敗的用例有重跑了2次,第二次失敗的用例就沒有重跑2次。測試
TestNg提供的重跑機制,實現思路以下:ui
IRetryAnalyzer
接口,重寫該接口的 retry
方法,定義失敗重跑的規則。IAnnotationTransformer
接口,重寫接口 transform
方法,用於監聽全部的@Test 註解的測試方法。package com.ggf.testng.listener; import org.testng.IRetryAnalyzer; import org.testng.ITestResult; /** * @Description: 失敗重試方法 * 用來監聽用例的執行狀況,若是斷言失敗,或者代碼出現錯誤了 * 都會被這個方法進行捕獲到,而後經過返回值來判斷是否進行重試。 * @Author: ggf * @Date: 2020/07/20 */ public class TestngRetry implements IRetryAnalyzer { /** * 最大的重跑次數 * 設置用例最多重跑多少次 */ private int maxRetryCount = 2; /** * 當前的重跑的次數 */ private int currentRetryCount = 1; /** * 複寫 IRetryAnalyzer 的方法,全部的用例執行完後的結果都會 * 封裝到這個對象ITestResult 傳入到 retry.xml 方法,經過這個方法 * 返回值來判斷是否須要從新執行用例。false :不重跑 true:重跑。 * @param iTestResult * @return */ @Override public boolean retry(ITestResult iTestResult) { //若是retry方法返回爲true--》執行重試機制 //若是返回是爲false --》不會去執行重試 //何時會運行到這裏??條件-->測試用例執行失敗 if(currentRetryCount <= maxRetryCount){ //運行了一次重試機制以後,咱們就加1 //若是運行第一次重試機制-->用例執行成功了,用例的結果是pass的 //若是運行第一次重試機制-->用例執行成功了,第二次重試機制不會運行 System.out.println("重跑第【"+currentRetryCount+"】次!"); currentRetryCount++; return true; }else{ return false; } } }
package com.ggf.testng.listener; import org.testng.IAnnotationTransformer; import org.testng.IRetryAnalyzer; import org.testng.annotations.ITestAnnotation; import java.lang.reflect.Constructor; import java.lang.reflect.Method; /** * @Description: * 因爲在使用重跑機制的時候須要在每一個用例@Test註解添加 retryAnalyzer 屬性。 * 若是用例量過大的話,很是的麻煩,因此咱們引入 testng 提供的監聽器類:IAnnotationTransformer * 經過這個監聽器類來實現,動態的修改@Test註解屬性,咱們就能夠統一給 @Test 註解動態加上屬性retryAnalyzer 值。 * @Author: ggf * @Date: 2020/07/20 */ public class RetryListener implements IAnnotationTransformer { @Override public void transform(ITestAnnotation iTestAnnotation, Class aClass, Constructor constructor, Method method) { //一、拿到@test註解的retryAnalyzer屬性對象 IRetryAnalyzer iRetryAnalyzer = iTestAnnotation.getRetryAnalyzer(); //二、若是@test的retryAnalyzer屬性沒有設置,iRetryAnalyzer-->null if(iRetryAnalyzer == null){ iTestAnnotation.setRetryAnalyzer(TestngRetry.class); } } }
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <suite name="retry"> <test name="retryTest"> <classes> <class name="com.ggf.testng.listener.RetryDemo"></class> </classes> </test> <listeners> <!--失敗重試監聽器--> <listener class-name="com.ggf.testng.listener.RetryListener"></listener> </listeners> </suite>
package com.ggf.testng.listener; import org.testng.Assert; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; /** * @Description: * @Author: ggf * @Date: 2020/07/20 */ public class RetryDemo { @Test(dataProvider = "data") public void testRetry(String data1, String data2) { // 斷言兩個參數是否同樣 Assert.assertEquals(data1, data2); } @DataProvider public Object[][] data() { // 提供兩組測試參數 return new Object[][]{{"111","123"}, {"123", "1234"}}; } }
對於一個使用了dataProvider
的用例,由於這個用例是一個標記爲@Test
的方法,會共用TestngRetry
的currentRetryCount
,即整個方法的全部參數化用例,總共只會重跑 2 次。例如一個參數化用例有 2 組參數,若是所有正確,每一個用例只會輸出一次:code
Test1: success Test2: success
若是兩組參數的用例都失敗了,對於第一組參數是會重跑2次的(代碼設置的是2次,不包含第一次),到了第二組參數就不會繼續重跑了,由於currentRetryCount
在第一組參數用例跑完,當前值就爲 3 了,這個時候不知足重跑條件,第二組參數用例失敗後就不會重跑了。orm
Test1: failed -> skipped Test1: failed -> skipped Test1: failed Test2: failed Test2: failed
至於這裏的 Test2 爲何會跑 2 次,沒搞明白,正常來講應該是跑一次的,由於程序中 if(currentRetryCount <= maxRetryCount)
` 知足纔會重跑,可是這裏是 false 因此不會重跑纔對。若是有大神們知道爲何,但願不吝賜教,留個言。。。xml
要解決上述的問題,須要在每組參數化用例結束(不管成功,失敗)後,重置 currentRetryCount
的值,讓當前的次數保持在初始化的狀態。對象
在實現類 TestngRetry 中加上一個重置的方法,以下:blog
package com.ggf.testng.listener; import org.testng.IRetryAnalyzer; import org.testng.ITestResult; /** * @Description: 失敗重試方法 * 用來監聽用例的執行狀況,若是斷言失敗,或者代碼出現錯誤了 * 都會被這個方法進行捕獲到,而後經過返回值來判斷是否進行重試。 * @Author: ggf * @Date: 2020/07/20 */ public class TestngRetry implements IRetryAnalyzer { /** * 最大的重跑次數 * 設置用例最多重跑多少次 */ private int maxRetryCount = 2; /** * 當前的重跑的次數 */ private int currentRetryCount = 1; /** * 複寫 IRetryAnalyzer 的方法,全部的用例執行完後的結果都會 * 封裝到這個對象ITestResult 傳入到 retry.xml 方法,經過這個方法 * 返回值來判斷是否須要從新執行用例。false :不重跑 true:重跑。 * @param iTestResult * @return */ @Override public boolean retry(ITestResult iTestResult) { //若是retry方法返回爲true--》執行重試機制 //若是返回是爲false --》不會去執行重試 //何時會運行到這裏??條件-->測試用例執行失敗 if(currentRetryCount <= maxRetryCount){ //運行了一次重試機制以後,咱們就加1 //若是運行第一次重試機制-->用例執行成功了,用例的結果是pass的 //若是運行第一次重試機制-->用例執行成功了,第二次重試機制不會運行 System.out.println("重跑第【"+currentRetryCount+"】次!"); currentRetryCount++; return true; }else{ return false; } } /** * 用於重置失敗重跑時的次數,還原到初始化的值 * 若是項目中是使用dataProvider註解來提供用例測試數據參數化的, * 那麼每一個@Test執行的時候都會共有重跑的次數。 * 例如:一個參數化用例有 3 組參數,若是所有正確,結果是:所有經過 * 若是第一組參數,第一次失敗(第二次成功,這裏就用掉了一次重跑的次數,currentRetryCount 就+1了) * 接着第二組參數每次執行都失敗,這個時候currentRetryCount=2, 那麼第二組參數也就只會執行一次重跑。 */ public void reset() { currentRetryCount = 1; } }
再新建一個監聽類 TestngListener
, 繼承 TestListenerAdapter
類,並重寫 onTestSuccess
和 onTestFailure
方法:繼承
package com.ggf.testng.listener; import org.testng.ITestContext; import org.testng.ITestNGMethod; import org.testng.ITestResult; import org.testng.TestListenerAdapter; import java.util.Iterator; /** * @Description: * @Author: ggf * @Date: 2020/07/20 */ public class TestngListener extends TestListenerAdapter { @Override public void onTestSuccess(ITestResult iTestResult) { super.onTestSuccess(iTestResult); TestngRetry testngRetry = (TestngRetry)iTestResult.getMethod().getRetryAnalyzer(); testngRetry.reset(); } @Override public void onTestFailure(ITestResult iTestResult) { super.onTestFailure(iTestResult); // 每次dataProvider中的參數跑完一次,就重置一次當前的重跑次數,恢復到默認值,保證每一個失敗的用例都能重跑設置的次數。 TestngRetry testngRetry = (TestngRetry)iTestResult.getMethod().getRetryAnalyzer(); testngRetry.reset(); } }
再把新建的TestngListener
監聽類,配置到 xml 文件中:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <suite name="retry"> <test name="retryTest"> <classes> <class name="com.ggf.testng.listener.RetryDemo"></class> </classes> </test> <listeners> <!--失敗重試監聽器--> <listener class-name="com.ggf.testng.listener.RetryListener"></listener> <!--用例執行結果監聽--> <listener class-name="com.ggf.testng.listener.TestngListener"></listener> </listeners> </suite>
再次執行的結果:
以上就是對於解決使用了dataProvider
用例中的每個參數化用例,在不重置的狀況下,用例重跑次數共用的問題。
最後咱們加上重置操做後,失敗的用例都會重跑跑 2 次,不管最後成功仍是失敗,都會重置 TestngRetry
中的currentRetryCount
以保證下一個參數化用例開始時,currentRetryCount
爲初始狀態。
參考文章:https://ntflc.com/2018/10/18/TestNg-Retry-Failed-Tests-with-DataProvider/