1. 在Testng.xml配置文件,在Test标签下增加监听
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="TC_Operation_Suite" parallel="none" preserve-order="true">
<test name="swiftcoder2_0.TC_Operation">
<groups>
<run>
<include name ="test.workflow" /> //执行初始条件
<include name ="groupa" /> //执行组名为groupa的所有用例
</run>
</groups>
<classes>
<class name = "swiftcoder2_0.TC_Operation" />//要哪个测试用例类,执行以上group
</classes>
<listeners>
<!-- put the listener class here -->
<listener class-name = "swiftcoder2_0.RetryListener" /> //增加RetryListener的监听
<listener class-name = "swiftcoder2_0.TestngListener" /> //增加TestngListener的监听
</listeners>
</test> <!-- Test -->
</suite> <!-- Suite -->
2.增加RetryListener类
package swiftcoder2_0;
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{
@Override
public void transform(ITestAnnotation annotation,
Class testClass, Constructor testConstructor, Method testMethod) {
// TODO Auto-generated method stub
IRetryAnalyzer retry = annotation.getRetryAnalyzer();
if(retry == null){
annotation.setRetryAnalyzer(TestngRetry.class); //设置RetryAnalyzer
}
}
}
3. 增加TestngRetry类
package swiftcoder2_0;
import org.testng.IRetryAnalyzer;
import org.testng.ITestResult;
import org.testng.log4testng.Logger;
import GL.GL;
public class TestngRetry implements IRetryAnalyzer{
private static Logger logger = Logger.getLogger(TestngRetry.class);
private int retryCount = 1;
private static int maxRetryCount;
@Override
public boolean retry(ITestResult result) {
//get the max retry count
maxRetryCount = Integer.parseInt(GL.getString("maxRunCount")); //在config.properties文件中设置maxRunCount 值
// TODO Auto-generated method stub
if (retryCount <= maxRetryCount) {
String message = "running retry for '" + result.getName() + "' on class " + this.getClass().getName() + " Retrying "
+ retryCount + " times";
logger.info(message);
retryCount++;
return true;
}
return false;
}
}
4. 增加TestngListener类
package swiftcoder2_0;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.testng.ITestContext;
import org.testng.ITestResult;
import org.testng.TestListenerAdapter;
import org.testng.log4testng.Logger;
public class TestngListener extends TestListenerAdapter{
private static Logger logger = Logger.getLogger(TestngListener.class);
@Override
public void onTestFailure(ITestResult tr){
super.onTestFailure(tr);
logger.info(tr.getName() + " Failure");
}
@Override
public void onTestSkipped(ITestResult tr){
super.onTestSkipped(tr);
logger.info(tr.getName() + " Skipped");
}
@Override
public void onTestSuccess(ITestResult tr){
super.onTestSuccess(tr);
logger.info(tr.getName() + " Success");
}
@Override
public void onTestStart(ITestResult tr){
super.onTestStart(tr);
logger.info(tr.getName() + " Start");
}
@Override
public void onFinish(ITestContext testContext){
//super.onFinish(testContext);
Iterator<ITestResult> listOfFailedTests = testContext.getFailedTests().getAllResults().iterator();
while(listOfFailedTests.hasNext()){
ITestResult failedTest = listOfFailedTests.next();
ITestNGMethod method = failedTest.getMethod();
if(testContext.getFailedTests().getResults(method).size()>1){
listOfFailedTests.remove();
}else{
if(testContext.getPassedTests().getResults(method).size()>0){
listOfFailedTests.remove();
}
}
}
}
}
5. 设置config.properties文件
maxRunCount=3
6. TC_Operation用例文件
package swiftcoder2_0;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Configuration;
import org.testng.annotations.Test;
import swiftcoder2_0.MultiOutputStream;
import swiftcoder2_0.Swiftcoder2_0;
import GL.GL;
import GL.Logs;
import swiftcoder2_0.TestngRetry;
public class TC_Operation {
//@variables
int c =0;
int a =0;
@BeforeTest
@Configuration(beforeTestClass= true, groups = {"test.workflow"})
public void SetUp() throws InterruptedException, IOException{
System.out.println("----Before Test--------");
}
@AfterTest
@Configuration(afterTestClass= true, groups = {"test.workflow"})
public void TearDown(){
System.out.println("----After Test--------");
}
@BeforeMethod
@Configuration(beforeTestMethod= true, groups = {"test.workflow"})
public void BeforeMethod() throws InterruptedException{
System.out.println("----Before Method--------");
}
@AfterMethod
@Configuration(afterTestMethod= true, groups = {"test.workflow"})
public void AfterMethod() throws InterruptedException{
//close the instance of the web driver
GL.quitWebDriver(wd);
System.out.println("----After Method--------");
}
@Test(groups = {"groupa"}, retryAnalyzer = TestngRetry.class)
public void testa(){
a++;
if(a <=2 ){
Assert.fail();
}
System.out.println("testa");
}
@Test(groups = {"groupa"} ,retryAnalyzer = TestngRetry.class)
public void testc(){
c++;
if(c <=3){
Assert.fail();
}
System.out.println("testc");
}
@Test(groups = {"groupb"}, retryAnalyzer = TestngRetry.class)
public void testb(){
System.out.println("testb");
}
@Test(groups = {"groupb"}, retryAnalyzer = TestngRetry.class)
public void testb_1(){
System.out.println("testb");
}
}
逻辑分析:
从testng_groups.xml加载RetryListener+TestngListener,2个监听类,在运行每个case后,调用
TestngRetry类和从TestngListener类执行结果,接着 将此结果传入到TestngRetry的retry函数中,如果Fail,当前用例连续执行最大次数为maxRunCount,如果Pass,则不需Retry调用。最后在TestngListener运行结果中删除同一case id no. 的重复用例,对每个用例结果只保留最后运行结果。
7. 运行结果
7.1 如果xml文件不加入TestngListener监听
[TestNG] Running:
E:\Documents\QA.Management\automation\swiftcoder2_0\testng-groups.xml
----Before Test--------
----Before Method--------
----After Method--------
----Before Method--------
----After Method--------
----Before Method--------
testa
----After Method--------
----Before Method--------
----After Method--------
----Before Method--------
----After Method--------
----Before Method--------
----After Method--------
----Before Method--------
testc
----After Method--------
----After Test--------
===============================================
TC_Operation_Suite
Total tests run: 7, Failures: 5, Skips: 0
7.2 如果xml文件加入TestngListener监听
[TestNG] Running:E:\Documents\QA.Management\automation\swiftcoder2_0\testng-groups.xml
This is log file
----Before Test--------
----Before Method--------
----After Method--------
----Before Method--------
----After Method--------
----Before Method--------
testa
----After Method--------
----Before Method--------
----After Method--------
----Before Method--------
----After Method--------
----Before Method--------
----After Method--------
----After Test--------
===============================================
TC_Operation_GroupsSuite
Total tests run: 2, Failures: 1, Skips: 0
===============================================
说明:若需要每个用例,需要监听,需要一一增加@Test(groups = {"groupb"},retryAnalyzer = TestngRetry.class)
如果测试时,用例Fail,最多执行maxRunCount次,用例Pass,无需重跑
xml文件加入TestngListener监听作用:首先解决TestNg生成的index.html文件中个数不对的问题,这个问题只需要在Testng监听器的onFinish方法中,等所有用例运行完之 后,检查用例,按照class+method+dataprodiver的名称生成hashcode获取唯一id,如果fail的用例中存在重复的则在 fail的用例中剔除掉。
参考资料:
http://testng.org/doc/documentation-main.html#test-groups
TestNG的官网资料 listeners的知识
http://www.ibm.com/developerworks/cn/opensource/os-cn-testinglistener/index.html
实战 TestNG 监听器
http://www.yeetrack.com/?p=1015
testng增加失败重跑机制
http://testng.org/javadoc/
JavaDoc API资料
https://martinholladay.wordpress.com/2013/11/16/testng-adjusting-test-counts-on-retry/
TESTNG & WEBDRIVER – ADJUSTING TEST COUNTS ON RETRY