Testng重试机制
测试中我们网络突然中断或者其他外界因素,导致本可以执行成功的用例也被记录为失败。所以,我们在自动化脚本中加入了用例失败重试机制,依靠监听用例执行的结果,进行重复执行,并且只会记录一次结果。
导入依赖
包含testng和Allurue报告
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.10</version>
</dependency>
<dependency>
<groupId>io.qameta.allure</groupId>
<artifactId>allure-testng</artifactId>
<version>2.13.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
<properties>
<allure.version>2.10.0</allure.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<aspectj.version>1.8.10</aspectj.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M1</version>
<configuration>
<!--设置参数命令行 -->
<argLine>
-javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar"
</argLine>
<systemPropertyVariables>
<!--是否忽略html,解释见下图。与之后在reportNg报告上显示截图相关。当前已经使用allure了,这里可以直接去掉啦 -->
<org.uncommons.reportng.escape-output>false</org.uncommons.reportng.escape-output>
<!--测试失败后,是否忽略并继续测试 -->
<testFailureIgnore>true</testFailureIgnore>
<argLine>
-Dfile.encoding=UTF-8
</argLine>
<property>
<name>usedefaultlisteners</name>
<value>false</value>
</property>
<property>
<name>listener</name>
<value>org.uncommons.reportng.HTMLReporter, org.uncommons.reportng.JUnitXMLReporter</value>
</property>
</systemPropertyVariables>
<suiteXmlFiles>
<!--代表的是要执行的测试套件名称 -->
<suiteXmlFile>test.xml</suiteXmlFile>
</suiteXmlFiles>
<workingDirectory>target</workingDirectory>
</configuration>
</plugin>
</plugins>
</build>
设置用例失败重试的次数
实现IRetryAnalyzer接口的retry方法。
package com.course.listener;
/**
* @author : Jack
* @Description: TODO
*/
import org.testng.IRetryAnalyzer;
import org.testng.ITestResult;
import org.testng.Reporter;
import org.testng.log4testng.Logger;
public class TestngRetry implements IRetryAnalyzer {
private static Logger logger = Logger.getLogger(TestngRetry.class);
private static int maxRetryCount = 3;//最大的重跑次数
private int retryCount = 1;
@Override
public boolean retry(ITestResult result) {
if (retryCount <= maxRetryCount) {
String message = "Running retry for '" + result.getName()
+ "' on class " + this.getClass().getName() + " Retrying "
+ retryCount + " times";
logger.info(message);
Reporter.setCurrentTestResult(result);
Reporter.log("RunCount=" + (retryCount + 1));
retryCount++;
return true;
}
return false;
}
}
使用方法一
在需要重试的方法上加上注解,如下:
public class TestRetry {
@Test(description = "重试测试",retryAnalyzer = TestngRetry.class)
public void test01(){
System.out.println("test01");
Assert.assertTrue(false,"测试失败");
}
@Test(description = "正常测试")
public void test02(){
System.out.println("test02");
Assert.assertTrue(true,"success");
}
}
使用方法二 (更便捷)
写一个监听类实现IAnnotationTransformer,在xml中或者类加上监听
package com.course.listener;
/**
* @author : Jack
* @Description: TODO
*/
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import org.testng.IAnnotationTransformer;
import org.testng.IRetryAnalyzer;
import org.testng.annotations.ITestAnnotation;
import org.testng.annotations.Listeners;
public class RetryListener implements IAnnotationTransformer {
@Override
public void transform(ITestAnnotation annotation,
Class testClass, Constructor testConstructor, Method testMethod) {
IRetryAnalyzer retry = annotation.getRetryAnalyzer();
if (retry == null) {
annotation.setRetryAnalyzer(TestngRetry.class);
}
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<suite name="test">
<test name="param">
<classes>
<class name="com.course.testng.TestRetry"/>
</classes>
</test>
<listeners>
<listener class-name="com.course.listener.RetryListener"></listener>
<listener class-name="com.course.listener.TestngListener"></listener>
<!-- <listener class-name="org.uncommons.reportng.HTMLReporter"/>-->
<!-- <listener class-name="org.uncommons.reportng.JUnitXMLReporter"/>-->
</listeners>
</suite>
解决一条用例执行多次后报告显示重复的问题
在跑多条用例时,testng会有一个bug:所有用例会公用同一个重试次数。这里需要重写onTestSuccess和onTestFailure接口
package com.course.listener;
/**
* @author : Jack
* @Project: Chapter5
* @date Date : 2020年07月15日 18:29
* @Description: TODO
*/
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.testng.ITestContext;
import org.testng.ITestResult;
import org.testng.TestListenerAdapter;
import org.testng.log4testng.Logger;
/***
*
*@Description:所有用例会公用同一个重试次数。这里需要重写onTestSuccess和onTestFailure接口
*@Author: Jack
*@Param:
*@Return:
*@Date: 2020/9/24 14:07
**/
public class TestngListener extends TestListenerAdapter {
private static Logger logger = Logger.getLogger(TestngListener.class);
@Override
public void onFinish(ITestContext testContext) {
super.onFinish(testContext);
// List of test results which we will delete later
ArrayList<ITestResult> testsToBeRemoved = new ArrayList<ITestResult>();
// collect all id's from passed test
Set<Integer> passedTestIds = new HashSet<Integer>();
for (ITestResult passedTest : testContext.getPassedTests()
.getAllResults()) {
logger.info("PassedTests = " + passedTest.getName());
passedTestIds.add(getId(passedTest));
}
Set<Integer> failedTestIds = new HashSet<Integer>();
for (ITestResult failedTest : testContext.getFailedTests()
.getAllResults()) {
logger.info("failedTest = " + failedTest.getName());
int failedTestId = getId(failedTest);
// if we saw this test as a failed test before we mark as to be
// deleted
// or delete this failed test if there is at least one passed
// version
if (failedTestIds.contains(failedTestId)
|| passedTestIds.contains(failedTestId)) {
testsToBeRemoved.add(failedTest);
} else {
failedTestIds.add(failedTestId);
}
}
// finally delete all tests that are marked
for (Iterator<ITestResult> iterator = testContext.getFailedTests()
.getAllResults().iterator(); iterator.hasNext();) {
ITestResult testResult = iterator.next();
if (testsToBeRemoved.contains(testResult)) {
logger.info("Remove repeat Fail Test: " + testResult.getName());
iterator.remove();
}
}
}
private int getId(ITestResult result) {
int id = result.getTestClass().getName().hashCode();
id = id + result.getMethod().getMethodName().hashCode();
id = id
+ (result.getParameters() != null ? Arrays.hashCode(result
.getParameters()) : 0);
return id;
}
}