testng重试机制

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;
    }
}


posted @ 2020-09-24 16:33  测开工程师成长之路  阅读(917)  评论(0编辑  收藏  举报