簡(jiǎn)單介紹
需求場(chǎng)景:測(cè)試移動(dòng)端應(yīng)用,常會(huì)因?yàn)辄c(diǎn)擊失效、網(wǎng)絡(luò)延遲大等原因?qū)е聹y(cè)試腳本失敗。這時(shí),需要自動(dòng)重新運(yùn)行失敗的腳本,直到腳本成功通過(guò)或者到達(dá)限定重試次數(shù)。
解決方案:實(shí)現(xiàn)testng的IRetryAnalyzer接口。
IRetryAnalyzer
IRetryAnalyzer是testng的一個(gè)接口,包含一個(gè)retry方法,用于實(shí)現(xiàn)失敗重試的功能。實(shí)現(xiàn)IRetryAnalyzer接口的代碼如下:
retry方法的用法是:返回true表示testcase重新運(yùn)行一次,反之,返回false。
通過(guò)自己定義的兩個(gè)變量retryCount和maxRetryCount來(lái)分別記錄重試的次數(shù)和多重試的次數(shù)。
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) {//這個(gè)函數(shù)將狀態(tài)碼轉(zhuǎn)換為狀態(tài)文字。
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文件。
方法一:通過(guò)注解失敗重試
修改testcase的注解,由@Test改為@Test(retryAnalyzer = Retry.class)。表示這個(gè)testcase使用了失敗重試的執(zhí)行策略。
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(){
}
}
輸出結(jié)果為:共運(yùn)行8個(gè)testcase,失敗了2個(gè)(demo和demo2失敗了),跳過(guò)5個(gè)(demo失敗后,重試了5次,都失敗了,標(biāo)記為“跳過(guò)”),還剩一個(gè)成功的是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
方法二:通過(guò)testng.xml失敗重試
與方法一比較,方法二需要再實(shí)現(xiàn)一個(gè)名為IAnnotationTransformer的接口。這個(gè)接口有一個(gè)transform方法,用來(lái)修改testcase的注解。這個(gè)方法的testannotation參數(shù)是testcase的注解。通過(guò)這個(gè)參數(shù)可以檢查注解中有沒(méi)有使用RetryAnalyzer,若沒(méi)有,則將自定義的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);//檢查注解中有沒(méi)有使用RetryAnalyzer,若沒(méi)有,則將自定義的Retry.class加入到注解中。
}
}
}
接下來(lái),還要在testng.xml中添加剛剛定義的RetryListener這個(gè)監(jiān)聽(tīng)器。
<!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的好處是,可以避免為每個(gè)需要重試的testcase添加注解,一切都在配置文件里完成。
這里把RetryListener這個(gè)監(jiān)聽(tīng)器應(yīng)用到了TestRetry這個(gè)類上,所以demo和demo2都會(huì)失敗重試。
輸出結(jié)果如下:
[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