• testNG retry 失败的testcase只需要在xml中配置一个listener即可


    问题情况                                                 

    先说下问题情况,最近在做testNG与selenium集成做自动化测试的问题。

    因为如果将testNG做UI 测试的话,很多情况下可能测试是失败的,但是这些失败可能是一些其他的问题导致的,可能是脚本的问题或者是网络环境不稳定导致的,所以我们需要重新尝试运行这个失败的测试用例。

    testNG倒是没有直接的retry testcase的功能,不过它却提供了很多的接口,我们可以实现这些接口来得到retry的效果。

    在google上看到淘宝的QA项目组采用Ruby语言将testNG的源代码修改了retry的功能,然后又重新build后这样做的。这是一个solution,但是我不推荐。原因有两个:

    1,修改的jar包是针对指定的testNG版本的,所以如果我们需要体验testNG的新版本功能,这个jar可能就需要在源码基本上重新build有点 不太合适,详细地址是:https://github.com/NetEase/Dagger/wiki/Retry-Failed-Or-Skipped-Testcases

    2,该种修改的方法只能使用在testcase级别上,如果需要针对所有的testNG的testsuite都是用这种特性,可能就需要每个testcase都表明他们是使用这个retry功能,有点代码亢余。像这样在testcase中声明retry的类:

    import org.apache.log4j.Logger;
    import org.testng.Assert;
    import org.testng.annotations.Test;
    
    import com.hp.baserunner.RetryFail;
    import com.hp.pop.DemoPage;
    
    public class DemoRun {
    
        private static Logger log=Logger.getLogger(DemoRun.class);
        @Test(retryAnalyzer=RetryFail.class)// 这里声明retry的类,可以看到如果这样每个testcase可能都需要这样做,代码是不是有点多啊 :(
        public void demoTest()
        {
            DemoPage dp=new DemoPage();
            dp.demoTest();
        }
        @Test
        public void demoTest2()
        {
            DemoPage dp2=new DemoPage();
            dp2.demoTest2();
        }
    }
    既然是框架这样写肯定就有点不太合适了。
    框架设计使用                                         
    testNG中的对应的提供这些功能的接口有这些:

    Interface IRetryAnalyzer   这个就是retrytestcase的一个接口,然后impletment这个接口后实现相应的方法即可:

    有一个类 RetryAnalyzerCount  已经实现了以上的这个接口的方法:

    package org.testng.util;
    
    import org.testng.IRetryAnalyzer;
    import org.testng.ITestResult;
    
    import java.util.concurrent.atomic.AtomicInteger;
    
    /**
     * An implementation of IRetryAnalyzer that allows you to specify
     * the maximum number of times you want your test to be retried.
     * 
     * @author tocman@gmail.com (Jeremie Lenfant-Engelmann)
     */
    public abstract class RetryAnalyzerCount implements IRetryAnalyzer {
    
      // Default retry once.
      AtomicInteger count = new AtomicInteger(1);
    
      /**
       * Set the max number of time the method needs to be retried.
       * @param count
       */
      protected void setCount(int count) {
        this.count.set(count);
      }
    
      /**
       * Retries the test if count is not 0. 
       * @param result The result of the test.
       */
      @Override
      public boolean retry(ITestResult result) {
        boolean retry = false;
    
        if (count.intValue() > 0) {
          retry = retryMethod(result);
          count.decrementAndGet();
        }
        return retry;
      }
    
      /**
       * The method implemented by the class that test if the test
       * must be retried or not.
       * @param result The result of the test.
       * @return true if the test must be retried, false otherwise.
       */
      public abstract boolean retryMethod(ITestResult result);
    }

     

    所以从上面可以看出,如果直接使用继承这个RetryAnalyzerCount 类还是省不少事,直接就可以使用了。

    Class TestListenerAdapter

    IConfigurationListener, IConfigurationListener2, org.testng.internal.IResultListener, org.testng.internal.IResultListener2, ITestListener, ITestNGListener

    上面的是另一个类实现了retry的操作的类。这里不使用。

    我们今天所使用的是IRetryAnalyzer 接口的,代码如下:

        package com.com.baserunner;
        import org.testng.IRetryAnalyzer;
        import org.testng.ITestResult;
        /**
         * @author sumeetmisri@gmail.com
         * @modify alterhu2020@gmail.com
         * @version 1.0
         * @category
         * 
         */
    
        public class RetryFail  implements IRetryAnalyzer
        {
            private final int m_maxRetries = 1;
            private final int m_sleepBetweenRetries = 1000;
            private int currentTry;
            private String previousTest = null;
            private String currentTest = null;
            public RetryFail()
            {
                currentTry = 0;
            }
    
            @Override
            public boolean retry(final ITestResult result)
            {
                // If a testcase has succeeded, this function is not called.        
                boolean retValue = false;        
                
                // Getting the max retries from suite.
               // String maxRetriesStr = result.getTestContext().getCurrentXmlTest().getParameter("maxRetries");
               String maxRetriesStr = result.getTestContext().getSuite().getParameter("maxRetries");
                int maxRetries = m_maxRetries;
                if(maxRetriesStr != null)
                {
                    try        
                    {
                        maxRetries = Integer.parseInt(maxRetriesStr);
                    }
                    catch (final NumberFormatException e)
                    {
                        System.out.println("NumberFormatException while parsing maxRetries from suite file." + e);
                    }
                }
               
                // Getting the sleep between retries from suite.you can from the suite parameter 
                String sleepBetweenRetriesStr = result.getTestContext().getSuite().getParameter("sleepBetweenRetries");
                int sleepBetweenRetries = m_sleepBetweenRetries;
                if(sleepBetweenRetriesStr != null)
                {
                    try        
                    {
                        sleepBetweenRetries = Integer.parseInt(sleepBetweenRetriesStr);
                    }
                    catch (final NumberFormatException e)
                    {
                        System.out.println("NumberFormatException while parsing sleepBetweenRetries from suite file." + e);
                    }
                }
                
                currentTest = result.getTestContext().getCurrentXmlTest().getName();
                
                if (previousTest == null)
                {
                    previousTest = currentTest;
                }
                if(!(previousTest.equals(currentTest)))
                {
                    currentTry = 0;
                }
               
                if (currentTry < maxRetries &&!result.isSuccess())
                {
                    try
                    {
                        Thread.sleep(sleepBetweenRetries);
                    }
                    catch (final InterruptedException e)
                    {
                        e.printStackTrace();
                    }
                    currentTry++;  
                    result.setStatus(ITestResult.SUCCESS_PERCENTAGE_FAILURE);
                    retValue = true;
                              
                }
                else
                {
                    currentTry = 0;
                }
                previousTest = currentTest;
                // if this method returns true, it will rerun the test once again.
                
             
                return retValue;
            }
        }

    还有一个lisetner需要加入到testNG的配置文件中:

    package com.coma.baserunner;
    
    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 {
    
        @SuppressWarnings("rawtypes")
        @Override
        public void transform(ITestAnnotation annotation, Class testClass,
                Constructor testConstructor, Method testMethod) {
    
            IRetryAnalyzer retry = annotation.getRetryAnalyzer();
            if (retry == null) {
                //annotation.setRetryAnalyzer(RetryAnalyzer.class);
                annotation.setRetryAnalyzer(RetryFail.class);
            }
        }
    
    }

    然后在testNG的xml的配置文件中如下配置即可:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
    <suite name="FirstSuite" parallel="false" >
     <!--  <parameter name="configfile" value="/resources/config.properties"></parameter> -->
     <parameter name="excelpath" value="resources/TestData.xls"></parameter>
     <listeners>
       <listener class-name="com.com.baserunner.RetryListener"></listener>
     </listeners> 

     

    以上的配置方法没有任何问题,唯一的缺陷是,运行的时候testNG的报告中会将retry的testcase的次数也计算在内,所以可能造成,运行后的testcase数目不准确,关于这个问题网上也有人在讨论,可是一直都没有得到一个好的接解决。

    最近觉得仔细看看testNG的源代码,看看能不能修改下对应的testNG的报告。使得结果显示的testcase数据与实际的一致,retry的testcase只计算最后一次运行成功的。

    如果有结果,再更新。。。。。。。Smile

  • 相关阅读:
    这 100 道 Python 题,拿去刷!!!
    快速入门 TensorFlow2 模型部署
    零基础入门 Kubernetes,你需要知道这些
    Java 面试必考难点,这一个教程全搞定
    Flask实战:如何从零开发“知乎”
    关于layui表格渲染templet解析单元格的问题
    清除input输入框的历史记录
    phpmyadmin导入导出大数据文件的办法
    yiiaseInvalidCallException The cookie collection is read only.
    Yii2.0关闭自带的debug功能
  • 原文地址:https://www.cnblogs.com/alterhu/p/3191701.html
Copyright © 2020-2023  润新知