@Test
用例失败重跑,retry最终一定是针对测试用例得,一个方法只有被@Test标注了才是测试用例,那么我们看下,@Test注解有没有相关属性呢?
retryAnalyzer,就是用于指定失败重跑得方案得,那么对应TestNG体系里提供了:
1.接口:IRetryAnalyzer
2.实现了接口得抽象类:
public abstract class RetryAnalyzerCount implements IRetryAnalyzer
所以,我们想实现失败自动重跑得方案就有了:
1.实现IRetryAnalyzer 接口
2.指定@Test得retryAnalyzer为我们得实现类
实现IRetryAnalyzer
我们已经知道IRetryAnalyzer接口有个抽象类RetryAnalyzerCount,那么我们可以自己重新实现IRetryAnalyzer,当然也可以直接继承RetryAnalyzerCount,这里我们选择直接继承RetryAnalyzerCount。
我们的RetryAnalyzer类,设置了重试计数为3,实现retryMethod,始终返回true。
我们看下RetryAnalyzerCount抽象类,它的关键是实现了retry方法,count就是重试计数值,获取count后减一,只要count大于0,看retryMethod方法的返回决定是否重试,只要retry的最后返回是true,就重新运行。
使用RetryAnalyzer
我们的RetryAnalyzer实现了,那么怎么使用呢?
@Test注解中有一个属性:retryAnalyzer,只要指定我们的重跑类,当case失败时,就可以触发重跑了。
我们看到case一共跑了4次,第一次跑完之后,case失败,于是重试3次,共4次,然后均失败,那么case最后failure,系统认为重试的case为Skip的,这里的统计这么显示是有一定道理,当然,测试报告也可以定制,我们后面也会讲到,(在早期版本的testng中,测试结果是有问题的,重试的用例不会认为是skip,而被算到failure中)。
虽然我们成功使用了自己的重跑类,但是每个@Test都去指定retryAnalyzer显得特别麻烦,怎么办呢?我们可以使用IAnnotationTransformer监听器,我们前面介绍过的,它是用来操作@Test注解的,我们实现这个监听器。
这个监听只有一个方法transform,我们通过annotation获取到属性retryanalyzer的值,如果没有设置,就设置其为我们自定义的重试类。这样只要指定使用AnnotationListener这个监听器,就可以默认我们的重试策略了。
我习惯通过ServiceLoader的方法使用监听器,创建一个配置文件:src\main\resources\META-INF\services\org.testng.ITestNGListener,并输入此自定义监听器。当然你也可以使用别的方法,请参考上一篇。
DataProvider
对于使用DataProvider数据驱动的case,我们发现,运行的时候就会出现问题,假设我们设置重试次数为2。
然后,我们看到结果:
第一组数据正常重试了两次,并置用例为失败,那么第二组数据,用例是失败的,可是却失败了2次?
我们先来确认,使用了DataProvider的用例失败重跑时,重跑次数不对的原因,是因为多组数据共用一个重试count,第一组数据跑完后,这个count就变成0了,后面的数据再想去重跑时,进入一次retry方法,count减一,其实此时count已经是负数了,所以就不能正常运行了,那么解决方法就比较有针对性了,只要保证每组数据的count都重置为初始设置就可以了。
我们知道,会改变count值的,只有失败的用例,成功用例不会进入重试策略,所以,需要在每个fail用例结尾,对count复位。我们想到方法:onTestFailure,这是ITestListener接口定义的方法,我们可以继承TestListenerAdapter,它是一个综合多个监听器接口的实现类。
对于失败重试类,我们增加一个复位方法reSetCount。
将这个监听器配置使用后,再来运行下看:
看到失败的用例都有重试2次了。
好了,那我们再回头看下,为什么一开始,第二组数据,用例失败了2次呢?我们通过debug模式追踪运行到Invoker类中的方法invokeTestMethods中,看到下面这段try...finally...代码。
首先我们明确,@Test方法被调用是在invokeMethod方法中,而下面的两个红框中的方法都调用了这个方法,finally的代码是一定会执行的,而且,由于前面进行了重试,failure.instances被add了值,不会是empty的(见下图willRetry的逻辑,failure.instances在要重试的时候被add,后面也不会被remove,始终非空了),一定会进入retryFailed方法,所以执行到这里测试方法就被掉用了2次,那么为什么count做了复位或者不用retry策略后,就正常了呢?
1.如果count做了复位,那么就属于正常进入retryFailed,因为失败用例会在做willRetry的时候算到skip中。
2.而不用retry策略,或者设置retrycount为0,那么failure.instance就是空的,因为我们看到failure.instance是在willRetry为true时被设置的,所以invokeTestMethods方法中finally的代码不会运行到retryFailed了。
测试报告
前面,我们已经提到过一次测试报告的数据展示了,再来看下:
我们发现,失败和成功的用例结果,数据是正确的,但是重试的测试用例,被划入skip数据中,同时,在run的总数据里也会体现,那么我们的理想状态应该是这样的,有几个case就认为run几次,重试的结果要么pass,要么failure,不算到skip中,当然也不算在总运行次数里。
怎么实现呢?
1.正常应该进入skip统计的用例,不去动,比如由于上级依赖case失败,而skip的部分
2.针对没有DataProvider的case,只要是重试skip的,最终一定是成功,或失败,所以在用例结果中,这个方法应该是有多个结果
3.而使用了数据驱动的,一个测试方法有多组数据在跑,不存在单组数据没有结果的skip,所以可以和2的情况一样考虑
那么我们的设计就是,在用例跑完的时候,将所有skip的用例结果取出,然后看看这些skip的用例方法有没有pass或者failure的结果,如果有,认为skip为重试导致的,将它去掉。
然后我们再来看下结果,最后输出结果正确了: