如何在TestNG中恢复失败的测试用例?

有时,案例失败,不是因为应用程序错误,而是因为意外事件,如浏览器问题,网络滞后等,现在,我们可能需要重新验证失败的案例,看看这些意外事件是否仍然存在。TestNG为我们提供了一些方法来做到这一点。

让我们一个接一个地看每一种方式。

使用testng-failed.xml文件重新运行失败的测试用例

让我们首先尝试运行一个测试类。我们的测试类将有三个测试用例。

package Test;
 
import org.testng.Assert;
import org.testng.annotations.Test;
 
public class CodekruTest {
 
    @Test
    public void test1() {
        System.out.println("Executing test1 in CodekruTest class");
        Assert.assertTrue(true);
    }
 
    @Test
    public void test2() {
        System.out.println("Executing test2 in CodekruTest class");
        Assert.assertTrue(false); // failing this test case
    }
 
    @Test
    public void test3() {
        System.out.println("Executing test3 in CodekruTest class");
        Assert.assertTrue(false); // failing this test case as well
    }
 
}

下面是运行上述测试类的XML文件

<suite name="codekru">
    <test name="codekruTest">
        <classes>
            <class name="Test.CodekruTest" />
        </classes>
    </test>
</suite>

运行上述XML文件后的输出-

Executing test1 in CodekruTest class

Executing test2 in CodekruTest class

Executing test3 in CodekruTest class

===============================================

codekru Total tests run: 3, Passes: 1, Failures: 2, Skips: 0 

 现在,有两个用例失败了(test 2和test 3),并且必须在testng-output中创建一个testng-failed.xml文件,该文件可用于运行失败的测试用例。

testng—failed.xml文件的内容包含再次运行失败案例所需的信息。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Failed suite [Failed suite [codekru]]" guice-stage="DEVELOPMENT">
  <test thread-count="5" name="codekruTest(failed)(failed)">
    <classes>
      <class name="Test.CodekruTest">
        <methods>
          <include name="test3"/>
          <include name="test2"/>
        </methods>
      </class> <!-- Test.CodekruTest -->
    </classes>
  </test> <!-- codekruTest(failed)(failed) -->
</suite> <!-- Failed suite [Failed suite [codekru]] -->

 运行testng-failed.xml文件后的输出-

[RemoteTestNG] detected TestNG version 7.4.0
Executing test3 in CodekruTest class
Executing test2 in CodekruTest class

===============================================
Failed suite [Failed suite [codekru]]
Total tests run: 2, Passes: 0, Failures: 2, Skips: 0

在这里,我们可以看到,只有失败的案件得到执行。

如果失败的用例依赖于其他通过的测试用例怎么办?
package Test;
 
import org.testng.Assert;
import org.testng.annotations.Test;
 
public class CodekruTest {
 
    @Test
    public void test1() {
        System.out.println("Executing test1 in CodekruTest class");
        Assert.assertTrue(true);
    }
 
    @Test(dependsOnMethods = "test4")
    public void test2() {
        System.out.println("Executing test2 in CodekruTest class");
        Assert.assertTrue(false); // failing this test case
    }
 
    @Test(dependsOnMethods = "test4")
    public void test3() {
        System.out.println("Executing test3 in CodekruTest class");
        Assert.assertTrue(false); // failing this test case as well
    }
 
    @Test
    public void test4() {
        System.out.println("Executing test4 in CodekruTest class");
        Assert.assertTrue(true);
    }
}

让我们再次运行CodekruTest类

Executing test1 in CodekruTest class
Executing test4 in CodekruTest class
Executing test2 in CodekruTest class
Executing test3 in CodekruTest class
PASSED: test4
PASSED: test1
FAILED: test3
FAILED: test2
===============================================
    Default test
    Tests run: 4, Failures: 2, Skips: 0
===============================================

 在这里,test 4用例通过,而test 2和test 3仍然失败。现在让我们看看testng-failed.xml文件的内容。

让我们在CodekruTest类中再包含一个测试用例(test4),test2和test3将使用dependsOnMethods属性依赖于test4。

package Test;
 
import org.testng.Assert;
import org.testng.annotations.Test;
 
public class CodekruTest {
 
    @Test
    public void test1() {
        System.out.println("Executing test1 in CodekruTest class");
        Assert.assertTrue(true);
    }
 
    @Test(dependsOnMethods = "test4")
    public void test2() {
        System.out.println("Executing test2 in CodekruTest class");
        Assert.assertTrue(false); // failing this test case
    }
 
    @Test(dependsOnMethods = "test4")
    public void test3() {
        System.out.println("Executing test3 in CodekruTest class");
        Assert.assertTrue(false); // failing this test case as well
    }
 
    @Test
    public void test4() {
        System.out.println("Executing test4 in CodekruTest class");
        Assert.assertTrue(true);
    }
}

让我们再次运行CodekruTest类

Executing test1 in CodekruTest class

Executing test4 in CodekruTest class

Executing test2 in CodekruTest class

Executing test3 in CodekruTest class

PASSED: test4 PASSED: test1 FAILED: test3 FAILED: test2 ===============================================

Default test Tests run: 4, Failures: 2, Skips: 0 ===============================================

 在这里,test 4用例通过,而test 2和test 3仍然失败。现在让我们看看testng-failed.xml文件的内容。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Failed suite [Default suite]" guice-stage="DEVELOPMENT">
  <test thread-count="5" name="Default test(failed)">
    <classes>
      <class name="Test.CodekruTest">
        <methods>
          <include name="test4"/>
          <include name="test3"/>
          <include name="test2"/>
        </methods>
      </class> <!-- Test.CodekruTest -->
    </classes>
  </test> <!-- Default test(failed) -->
</suite> <!-- Failed suite [Default suite] -->

我们可以看到test 4也包含在testng-failed.xml文件中,因为test 2和test 3依赖于test 4。因此,现在在执行testng-failed.xml文件后将运行三个测试用例。

Executing test4 in CodekruTest class

Executing test3 in CodekruTest class

Executing test2 in CodekruTest class ===============================================

Failed suite [Default suite] Total tests run: 3, Passes: 1, Failures: 2, Skips: 0 ===============================================

使用RetryAnalyzer重新运行失败的案例

通过testng-failed.xml运行失败的用例是一个很好的方法来验证测试用例,但是失败的用例将在TestNG执行整个测试套件之后执行。TestNG为我们提供了一个特性,它可以在失败时立即恢复我们的用例,我们可以通过使用retryAnalyzer属性来实现这一点。

使用retryAnalyzer属性的一些优点

  • 一旦失败,就会立即执行失败的用例,而无需等待TestNG执行整个套件。
  • 我们可以重试失败的次数,只要我们想要的。

那么,我们如何在代码中使用重试分析器呢?

  • 创建一个实现IRetryAnalyzer接口的类。
  • 将实现IRetryAnalyzer接口的类绑定到@Test注释。例如& nbsp;@Test(retryAnalyzer = Retry.class)

让我们创建一个实现IRetryAnalyzer接口的类。IRetryAnalyzer有一个retry()方法,我们的类将覆盖该方法。

package Test;
 
import org.testng.IRetryAnalyzer;
import org.testng.ITestResult;
 
public class Retry implements IRetryAnalyzer {
 
    int counter = 0;
    int retryLimit = 2; // maximum number of times, the case can be retried
 
    public boolean retry(ITestResult result) {
        if (counter < retryLimit) {
            counter++;
            return true; // retry the case if retryLimit is not exhausted
        }
        return false; // Do not retry anymore, as retryLimit is exhausted
    }
 
}

 

  • 在这里,计数器变量存储了一个案例被执行的次数。
  • retryLimit变量告知单个测试用例的最大重试次数。

它将重试该案件,直到案件通过或重试已用尽。现在,我们将在我们的测试类(CodekruTest)中使用它。

package Test;
 
import org.testng.Assert;
import org.testng.annotations.Test;
 
public class CodekruTest {
 
    @Test
    public void test1() {
        System.out.println("Executing test1 in CodekruTest class");
        Assert.assertTrue(true);
    }
 
    @Test(retryAnalyzer = Retry.class)
    public void test2() {
        System.out.println("Executing test2 in CodekruTest class");
        Assert.assertTrue(false); // failing this test case
    }
 
    @Test()
    public void test3() {
        System.out.println("Executing test3 in CodekruTest class");
        Assert.assertTrue(false); // failing this test case as well
    }
}

 现在,test2()用例在失败后将重试2次。所以,现在让我们尝试使用下面的XML文件运行我们的CodekruTest类。

<suite name="codekru">
    <test name="codekruTest">
        <classes>
            <class name="Test.CodekruTest" />
        </classes>
    </test>
</suite>

 

产出-

Executing test1 in CodekruTest class
Executing test2 in CodekruTest class
Executing test2 in CodekruTest class
Executing test2 in CodekruTest class
Executing test3 in CodekruTest class

===============================================
codekru
Total tests run: 5, Passes: 1, Failures: 2, Skips: 0, Retries: 2

我们可以看到test2()已经重试了两次。

如何对一个类的所有案例进行重试?

有时候,对每个用例都使用retryAnalyzer需要花费时间,尤其是当类有很多测试用例的时候。为了保存我们,TestNG为我们提供类级注释的功能。我们只能在类级别上放置@Test((retryAnalyzer = Retry.class)一次,它将应用于类中的所有案例。

package Test;
 
import org.testng.Assert;
import org.testng.annotations.Test;
 
@Test(retryAnalyzer = Retry.class)
public class CodekruTest {
 
    public void test1() {
        System.out.println("Executing test1 in CodekruTest class");
        Assert.assertTrue(true);
    }
 
    public void test2() {
        System.out.println("Executing test2 in CodekruTest class");
        Assert.assertTrue(false); // failing this test case
    }
 
    public void test3() {
        System.out.println("Executing test3 in CodekruTest class");
        Assert.assertTrue(false); // failing this test case as well
    }
}

现在,所有的测试用例如果失败,将被重试。让我们通过执行上面的类来尝试一下。

Executing test1 in CodekruTest class
Executing test2 in CodekruTest class
Executing test2 in CodekruTest class
Executing test2 in CodekruTest class
Executing test3 in CodekruTest class
Executing test3 in CodekruTest class
Executing test3 in CodekruTest class

test2()和test3()又执行了两次。

如何对一套件的所有案件进行重试?

现在让我们讨论一下如何在套件中运行的所有案例上设置重试?

我们可以通过使用IAnnotationTransformer接口来实现这一点,在该接口中我们可以实现transform方法。我们将使用一个类(比如AnnotationTransformer)来实现IAnnotationTransformer接口。

这个接口用于在运行时以编程方式添加注释,并且它的transform方法在每个测试用例运行时执行。因此,我们将重写transform方法,并在运行时在我们的测试用例中添加retryAnalyzer功能。

package Test;
 
import org.testng.IAnnotationTransformer;
import org.testng.annotations.ITestAnnotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
 
public class AnnotationTransformer implements IAnnotationTransformer {
 
    public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
        annotation.setRetryAnalyzer(Retry.class); // setting the retryAnalyzer
    }
 
}

现在,如何使用AnnotationTransformer类,以便它可以将retryAnalyzer功能添加到我们的测试用例中。我们必须在testng.xml文件中添加这个类作为侦听器,然后由XML文件运行的所有案例都将添加retryAnalyzer属性。

现在,我们的CodekruTest类看起来像

package Test;
 
import org.testng.Assert;
import org.testng.annotations.Test;
 
public class CodekruTest {
 
    @Test
    public void test1() {
        System.out.println("Executing test1 in CodekruTest class");
        Assert.assertTrue(true);
    }
 
    @Test
    public void test2() {
        System.out.println("Executing test2 in CodekruTest class");
        Assert.assertTrue(false); // failing this test case
    }
 
    @Test
    public void test3() {
        System.out.println("Executing test3 in CodekruTest class");
        Assert.assertTrue(false); // failing this test case as well
    }
}

 我们的XML文件将是

<suite name="codekru">
    <listeners>
        <listener class-name="Test.AnnotationTransformer" />
    </listeners>
    <test name="codekruTest">
        <classes>
            <class name="Test.CodekruTest" />
        </classes>
    </test>
</suite>

 

运行上述XML文件后的输出-

Executing test1 in CodekruTest class
Executing test2 in CodekruTest class
Executing test2 in CodekruTest class
Executing test2 in CodekruTest class
Executing test3 in CodekruTest class
Executing test3 in CodekruTest class
Executing test3 in CodekruTest class

===============================================
codekru
Total tests run: 7, Passes: 1, Failures: 2, Skips: 0, Retries: 4
===============================================

我们可以看到,失败的案例被自动重试。

最后一件重要的事

您可能已经注意到,每当我们重试案例时,我们的总案例数就会增加,跳过的案例数也会增加。以下是我们上次执行死刑的总结

注意:TestNG的新版本将Retries字段添加到摘要中。

Executing test1 in CodekruTest class
Executing test2 in CodekruTest class
Executing test2 in CodekruTest class
Executing test2 in CodekruTest class
Executing test3 in CodekruTest class
Executing test3 in CodekruTest class
Executing test3 in CodekruTest class

===============================================
codekru
Total tests run: 7, Passes: 1, Failures: 2, Skips: 0, Retries: 4
===============================================

test case summary after retry

我们可以看到跳过的案例或重试计数为4,总案例计数为7。但是有时候,我们不想把重试算在所有的用例中,而只想显示在我们的测试类中已经写好的用例的数量。

那么,如何在所有情况下不计算重试次数呢?

在这里,我们将不得不使用多一个接口,ITestrons,和一个类(比如TestListenerClass)来实现接口。

package Test;
 
import org.testng.ITestListener;
import org.testng.ITestResult;
 
public class TestListenerClass implements ITestListener {
 
    public void onTestSuccess(ITestResult result) {
 
        if (result.getMethod().getRetryAnalyzer(result) != null) {
            int numberOfSkippedCases = result.getTestContext().getSkippedTests().getResults(result.getMethod()).size();
 
            for (int i = 0; i < numberOfSkippedCases; i++) {
                result.getTestContext().getSkippedTests().removeResult(result.getMethod());
            }
        }
 
    }
     
    public void onTestFailure(ITestResult result) {
 
        if (result.getMethod().getRetryAnalyzer(result) != null) {
            int numberOfSkippedCases = result.getTestContext().getSkippedTests().getResults(result.getMethod()).size();
 
            for (int i = 0; i < numberOfSkippedCases; i++) {
                result.getTestContext().getSkippedTests().removeResult(result.getMethod());
            }
        }
 
    }
 
}

 

那我们来这里干什么

  • onTestFailure()方法在每次用例失败时执行,而onTestSuccess()方法在测试用例通过时执行。
  • 因此,无论测试用例是通过还是失败,我们都将使用result.getMethod().getRetryAnalyzer(result)!=空条件。
  • 如果测试用例设置了retryAnalyzer属性,我们将计算跳过的用例的数量。因为每次我们重试一个案例,它都被标记为跳过。
  • 一旦我们知道了漏查的案子。我们将运行一个for循环来从结果中删除跳过的案例。

现在,我们必须在XML文件中添加TestListenerClass作为侦听器。因此,我们更新的XML文件将是

<suite name="codekru">
    <listeners>
        <listener class-name="Test.AnnotationTransformer" />
        <listener class-name="Test.TestListenerClass" />
    </listeners>
    <test name="codekruTest">
        <classes>
            <class name="Test.CodekruTest" />
        </classes>
    </test>
</suite>

运行上述XML文件后的输出,它再次执行我们的CodekruTest类的案例-

Executing test1 in CodekruTest class
Executing test2 in CodekruTest class
Executing test2 in CodekruTest class
Executing test2 in CodekruTest class
Executing test3 in CodekruTest class
Executing test3 in CodekruTest class
Executing test3 in CodekruTest class

===============================================
codekru
Total tests run: 3, Passes: 1, Failures: 2, Skips: 0
===============================================

这里我们可以看到,显示的计数仅为3,而不是7。

尽管TestNG摘要看起来仍然像

test case summary after retry

 

                                                                                                                                                                                                                                                                                                                                                           

相关推荐

  1. 如何seata编写测试

    2024-05-01 08:38:03       12 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-05-01 08:38:03       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-05-01 08:38:03       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-05-01 08:38:03       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-05-01 08:38:03       18 阅读

热门阅读

  1. Seata分布式原理及优势

    2024-05-01 08:38:03       13 阅读
  2. 鼠标界面的隐藏显示

    2024-05-01 08:38:03       7 阅读
  3. 安卓手机APP开发__媒体开发部分__媒体投屏

    2024-05-01 08:38:03       12 阅读
  4. 【Spring】4.Spring的事务管理解析

    2024-05-01 08:38:03       10 阅读
  5. 学习 Rust 的第十五天:如何处理程序异常信息

    2024-05-01 08:38:03       9 阅读
  6. ThinkPHP8 导出Excel数据表格

    2024-05-01 08:38:03       10 阅读
  7. vue3中 使用vue-types

    2024-05-01 08:38:03       7 阅读