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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM