在使用 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/