TestNG测试用例重跑详解及实践优化

发表于:2020-5-14 10:37

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:Edison Zhou    来源:恰童鞋骚年

  测试用例运行稳定性是自动化质量的一个重要指标,在运行中需要尽可能的剔除非bug造成的测试用例执行失败,对于失败用例进行重跑是常用策略之一。一种重跑策略是所有用例运行结束后对失败用例重跑,另一种重跑策略是在运行时监控用例运行状态,失败后实时重跑。
  下面,详细介绍TestNG如何对失败测试用例实时重跑并解决重跑过程中所遇到问题的实践和解决方案。对失败测试用例进行实时重跑,有以下几个方面需求:
  测试用例运行失败,监听到失败后立即进行重跑
  测试用例通过dependsOnMethods/dependsOnGroups标记依赖其他测试用例,在被依赖的测试用例重跑运行成功后,该测试用例可以继续运行
  对于重跑多次的测试用例,只记录最后一次运行成功或失败结果
  第一部分 测试用例重跑
  1.1 retryAnalyzer注解方式
  对于希望测试用例中的少量易失败,不稳定的测试用例进行重跑,可采用这种方式。
  1.1.1 原理
  以下是TestNG处理测试用例运行结果的部分代码。
   IRetryAnalyzer retryAnalyzer = testMethod.getRetryAnalyzer();
  boolean willRetry = retryAnalyzer != null && status == ITestResult.FAILURE && failure.instances != null && retryAnalyzer.retry(testResult);
  if (willRetry) {
  resultsToRetry.add(testResult);
  failure.count++;
  failure.instances.add(testResult.getInstance());
  testResult.setStatus(ITestResult.SKIP);
  } else {
  testResult.setStatus(status);
  if (status == ITestResult.FAILURE && !handled) {
  handleException(ite, testMethod, testResult, failure.count++);
  }
  分析以上代码,其中,接口IretryAnalyzer的方法retry()的返回值作为是否对失败测试用例进行重跑的一个条件。如果retry()结果为true,则该失败测试用例会重跑,同时将本次失败结果修改为Skip;如果结果为false,则失败的测试用例保持失败结果,运行结束。因此,如果你希望失败测试用例重跑的话,需要把IretryAnalyzer的retry()方法重写,插入自己定义的逻辑,设置返回值为true。
  1.1.2 代码
  创建类RetryImpl,重写retry()方法,设置失败测试用例的重跑次数,代码如下,:
   public class RetryImpl implements IRetryAnalyzer {
  private int count = 1;
  private int max_count = 3;   // Failed test cases could be run 3 times at most
  @Override
  public boolean retry(ITestResult result) {
  System.out.println("Test case :"+result.getName()+",retry time: "+count+"");
  if (count < max_count) {
  count++;
  return true;
  }
  return false;
  }
  }
  1.1.3 实例
   public class TestNGReRunDemo {
  @Test(retryAnalyzer=RetryImpl.class)
  public void test01(){
  Assert.assertEquals("success","fail");
  System.out.println("test01");
  }
  }
  以上测试用例test01可重复运行3次。
  1.2 实现接口IAnnotationTransformer方法
  如果希望所有失败的测试用例都进行重跑,采用retryAnalyzer注解方式对每个测试用例进行注解就比较麻烦。通过实现IAnnotationTransformer接口的方式,可以对全量测试用例的重试类进行设置。
  该接口是一个监听器接口,用来修改TestNG注解。IAnnotationTransformer监听器接口只有一个方法:transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod). 上文中,我们自定义了类RetryImpl 实现接口IRetryAnalyzer。TestNG通过transfrom()方法修改retryAnalyzer注解。以下代码对retryAnalyzer注解进行修改设置。
  1.2.1代码
  创建类RetryListener,代码如下。
   public class RetryListener implements IAnnotationTransformer {
  public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
  IRetryAnalyzer retry = annotation.getRetryAnalyzer();
  if (retry == null) {
  annotation.setRetryAnalyzer(RetryImpl.class);
  }
  }
  }
  1.2.2 配置Listener
  TestNG可以在配置文件或者测试类中对Listener类进行配置。
  方法一:在TestNG的配置XML中进行以下配置
   <listeners>
  <listener class-name="PackageName.RetryListener"></listener>
  </listeners>
  方法二:在测试类中通过@Listeners配置
   @Listeners({RetryListener.class})
  public class TestNGReRunDemo {
  @Test
  public void test01(){
  Assert.assertEquals("success","fail");
  System.out.println("test01");
  }
  }
  配置完成后,运行测试用例test01,运行结果显示test01将重跑次数3次。
  第二部分 被依赖的测试用例重跑结果处理
  进一步分析TestNG的运行代码,其在对失败运行用例重跑时,逻辑如下图。
  对于通过dependsOnMethods 或dependsOnGroups注解依赖于其他测试用例的测试用例来讲,测试用例执行分为两种情况:
  alwaysRun=true,则无论所依赖的测试用例执行情况如何,该测试用例都会执行,即所依赖的测试用例重跑不会影响该测试用例的执行。
  alwaysRun=false,或者保持缺省值(false),依赖于其他测试用例或测试用例组的测试结果,在运行时TestNG获取所依赖的测试用例的运行结果,检查依赖的测试用例是否全部执行成功,如果不全部成功,则把该测试用例结果设置为Skipped。
  2.1 场景分析:场景一
  被依赖的测试用例失败后进行了重跑,并重跑成功。(注:在RetryImpl类中,已设置最大重跑次数max_count = 3)
   public static int number =0;
  @Test
  public void test01(){
  number++;
  System.out.println(String.valueOf(number));
  Assert.assertEquals(number,2);
  System.out.println("test01");
  }
  @Test(dependsOnMethods = "test01")    // alwaysRun = false by default
  public void test02(){
  System.out.println("test02 is running only if test01 is passed.");
  }
  1、TestNG测试报告
  2、问题
  测试报告:test01运行结果全部被记录,而用例重跑,只希望记录最后的结果。
  运行情况:测试用例test02依赖于测试用例test01运行结果,在test01重跑成功后,测试用例test02没有执行,不符合需求预期。
  2.2 场景分析:场景二
  被依赖的测试用例失败后进行了重跑,并且重跑没有成功。(注:在RetryImpl类中,已设置最大重跑次数max_count = 3)
   public static int number =0;
  @Test
  public void test01(){
  number++;
  System.out.println(String.valueOf(number));
  Assert.assertEquals(number,10);
  System.out.println("test01");
  }
  @Test(dependsOnMethods = "test01")    // alwaysRun = false by default
  public void test02(){
  System.out.println("test02 is running only if test01 is passed.");
  }
  1、TestNG测试报告
  2、问题
  运行情况:测试用例test02依赖于测试用例test01运行结果,在test01重跑失败后,测试用例test02没有执行,这种情况符合需求预期。
  测试报告:同场景一,test01重跑失败,运行结果全部被记录,而用例重跑,只希望记录最后的结果。
  第三部分 优化解决方案
  以下方案解决重跑测试用例成功后后继测试用例无法继续运行的问题,并对测试报告进行优化。
  3.1 TestListenerAdapter方法重写
  根据上面分析的TestNG逻辑,在对依赖测试用例的结果进行检查时,如果忽略重跑的中间结果只检查最后一次的运行结果,可以达到需求的目的。对于测试报告,同样的处理方式,忽略所有中间的测试用例运行结果,只记录最后结果。
  测试用例的中间运行结果为Skipped,下面的代码通过重写TestListenerAdapter的onTestSuccess()和onTestFailure()方法,对测试用例的中间结果skipped进行了删除。代码如下:
   public class ResultListener extends TestListenerAdapter {
  @Override
  public void onTestFailure(ITestResult tr) {
  if(tr.getMethod().getCurrentInvocationCount()==1)
  {
  super.onTestFailure(tr);
  return;
  }
  processSkipResult(tr);
  super.onTestFailure(tr);
  }
  @Override
  public void onTestSuccess(ITestResult tr) {
  if(tr.getMethod().getCurrentInvocationCount()==1)
  {
  super.onTestSuccess(tr);
  return;
  }
  processSkipResult(tr);
  super.onTestSuccess(tr);
  }
  // Remove all the dup Skipped results
  public void processSkipResult(ITestResult tr)
  {
  ITestContext iTestContext = tr.getTestContext();
  Iterator<ITestResult> processResults = iTestContext.getSkippedTests().getAllResults().iterator();
  while (processResults.hasNext()) {
  ITestResult skippedTest = (ITestResult) processResults.next();
  if (skippedTest.getMethod().getMethodName().equalsIgnoreCase(tr.getMethod().getMethodName()) ) {
  processResults.remove();
  }
  }
  }
  }
  3.2 配置结果处理Listener类
  在配置文件进行全局设置或者在测试类中标记。
  方法一:在TestNG的配置XML中进行以下配置
   <listeners>
  <listener class-name="PackageName.ResultListener"></listener>
  </listeners>
  方法二:在测试类中通过@Listeners配置
   @Listeners({ResultListener.class})
  public class TestNGReRunDemo {
  @Test
  public void test01(){
  Assert.assertEquals("success","fail");
  System.out.println("test01");
  }
  }
  3.3 场景一
  1、 结果验证
  2、结果分析:
  3.4 场景二
  1、结果验证
  2、结果分析:

      本文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号