简单介绍
需求场景:测试移动端应用,常会因为点击失效、网络延迟大等原因导致测试脚本失败。这时,需要自动重新运行失败的脚本,直到脚本成功通过或者到达限定重试次数。
解决方案:实现testng的IRetryAnalyzer接口。
IRetryAnalyzer
IRetryAnalyzer是testng的一个接口,包含一个retry方法,用于实现失败重试的功能。实现IRetryAnalyzer接口的代码如下:
retry方法的用法是:返回true表示testcase重新运行一次,反之,返回false。
通过自己定义的两个变量retryCount和maxRetryCount来分别记录重试的次数和最多重试的次数。
1 package main.java.com.dbyl.library.utils; 2 3 /** 4 * Created by wwh on 17/2/23. 5 */ 6 import org.testng.IRetryAnalyzer; 7 import org.testng.ITestResult; 8 9 public class Retry implements IRetryAnalyzer { 10 private int retryCount = 0; 11 private int maxRetryCount = 5; 12 13 // Below method returns 'true' if the test method has to be retried else 'false' 14 //and it takes the 'Result' as parameter of the test method that just ran 15 public boolean retry(ITestResult result) { 16 if (retryCount < maxRetryCount) { 17 System.out.println("Retrying test " + result.getName() + " with status " 18 + getResultStatusName(result.getStatus()) + " for the " + (retryCount+1) + " time(s)."); 19 retryCount++; 20 return true; 21 } 22 return false; 23 } 24 25 public String getResultStatusName(int status) {//这个函数将状态码转换为状态文字。 26 String resultName = null; 27 if(status==1) 28 resultName = "SUCCESS"; 29 if(status==2) 30 resultName = "FAILURE"; 31 if(status==3) 32 resultName = "SKIP"; 33 return resultName; 34 } 35 }
使用方法
有两种方法使用上面定义的Retry.class:一种是注解,另一种是借助于testng.xml文件。
方法一:通过注解失败重试
修改testcase的注解,由@Test改为@Test(retryAnalyzer = Retry.class)。表示这个testcase使用了失败重试的执行策略。
package main.java.com.dbyl.library.utils; import org.testng.Assert; import org.testng.annotations.Test; /** * Created by wwh on 17/2/23. */ public class TestRetry { @Test(retryAnalyzer = Retry.class) public void Demo() { Assert.fail(); } @Test public void Demo2(){ Assert.fail(); } @Test public void Demo3(){ } }
输出结果为:共运行8个testcase,失败了2个(demo和demo2失败了),跳过5个(demo失败后,重试了5次,都失败了,标记为“跳过”),还剩一个成功的是demo3。
[TestNG] Running: /Users/wwh/Library/Caches/IdeaIC2016.3/temp-testng-customsuite.xml Retrying test Demo with status FAILURE for the 1 time(s). Test ignored. Retrying test Demo with status FAILURE for the 2 time(s). Test ignored. Retrying test Demo with status FAILURE for the 3 time(s). Test ignored. Retrying test Demo with status FAILURE for the 4 time(s). Test ignored. Retrying test Demo with status FAILURE for the 5 time(s). Test ignored. java.lang.AssertionError: null at org.testng.Assert.fail(Assert.java:94) at org.testng.Assert.fail(Assert.java:101) 。
。
。 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) =============================================== Default Suite Total tests run: 8, Failures: 2, Skips: 5 =============================================== Process finished with exit code 0
方法二:通过testng.xml失败重试
与方法一比较,方法二需要再实现一个名为IAnnotationTransformer的接口。这个接口有一个transform方法,用来修改testcase的注解。这个方法的testannotation参数是testcase的注解。通过这个参数可以检查注解中有没有使用RetryAnalyzer,若没有,则将自定义的Retry.class加入到注解中。
package main.java.com.dbyl.library.utils; /** * Created by wwh on 17/2/23. */ import java.lang.reflect.Constructor; import java.lang.reflect.Method; import org.testng.IAnnotationTransformer; import org.testng.IRetryAnalyzer; import org.testng.annotations.ITestAnnotation; public class RetryListener implements IAnnotationTransformer { public void transform(ITestAnnotation testannotation, Class testClass, Constructor testConstructor, Method testMethod) { IRetryAnalyzer retry = testannotation.getRetryAnalyzer(); if (retry == null) { testannotation.setRetryAnalyzer(Retry.class);//检查注解中有没有使用RetryAnalyzer,若没有,则将自定义的Retry.class加入到注解中。 } } }
接下来,还要在testng.xml中添加刚刚定义的RetryListener这个监听器。
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" > <suite name="Second suite" verbose="1" > <listeners> <listener class-name="main.java.com.dbyl.library.utils.RetryListener"></listener> </listeners> <test name = "allTestsInAClass" > <classes> <class name="main.java.com.dbyl.library.utils.TestRetry"/> </classes> </test> </suite>
使用的testng.xml的好处是,可以避免为每个需要重试的testcase添加注解,一切都在配置文件里完成。
这里把RetryListener这个监听器应用到了TestRetry这个类上,所以demo和demo2都会失败重试。
输出结果如下:
[TestNG] Running: /Users/wwh/IdeaProjects/ProjectWang/src/main/resources/testng.xml Retrying test Demo with status FAILURE for the 1 time(s). Test ignored. Retrying test Demo with status FAILURE for the 2 time(s). Test ignored. Retrying test Demo with status FAILURE for the 3 time(s). Test ignored. Retrying test Demo with status FAILURE for the 4 time(s). Test ignored. Retrying test Demo with status FAILURE for the 5 time(s). Test ignored. java.lang.AssertionError: null at org.testng.Assert.fail(Assert.java:94) 。
。
。 at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) Retrying test Demo2 with status FAILURE for the 1 time(s). Test ignored. Retrying test Demo2 with status FAILURE for the 2 time(s). Test ignored. Retrying test Demo2 with status FAILURE for the 3 time(s). Test ignored. Retrying test Demo2 with status FAILURE for the 4 time(s). Test ignored. Retrying test Demo2 with status FAILURE for the 5 time(s). Test ignored. java.lang.AssertionError: null at org.testng.Assert.fail(Assert.java:94) at org.testng.Assert.fail(Assert.java:101) 。
。
。
at java.lang.reflect.Method.invoke(Method.java:498) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) =============================================== Second suite Total tests run: 13, Failures: 2, Skips: 10 =============================================== Process finished with exit code 0