【问题标题】:How to optimize testng and seleniums tests如何优化 testng 和 seleniums 测试
【发布时间】:2011-12-09 20:40:32
【问题描述】:

在我的实习中,我必须使用 TestNG 和 selenium 来测试 Web 应用程序。但是我有一个问题,有时硒或浏览器由于某种随机原因无法正常工作,因此工作测试被标记为“失败”。为了避免这种情况,我可以使用注解@Test(invocationCount = 4, successPercentage = 25),然后如果测试成功一次,则测试标记为“成功”,这很好,但问题是这个解决方案将测试时间乘以4,这不是非常有效。

我可以做的是减少测试时间,是写一些规则“如果测试失败,重新运行这个测试(只有当测试失败时),如果它在第二个,第三个或第四个工作时间,然后将此测试标记为“成功”“这样我就可以避免这些随机错误。但是我还没有找到如何写这个规则,我看到我们可以添加一个监听器,所以我们有一个名为“onTestFailure”的方法,所以我可以在测试失败但我不知道如何做的时候做点什么重新运行测试。

我还找到了 testng-failed.xml 保存了所有失败的测试,所以我们可以运行这个 xml 文件来重新运行这些测试,但这会删除之前第一次运行的报告,但我只想要失败的如果第二次运行成功,测试将被标记为“成功”。 (我已经将 testNG/selenium 集成到 Jenkins,所以我有一个包含所有测试的图表,所以这种方法不是很适应,但是这种方法不会将测试时间乘以 4,这就是我想要的)

因此,如果您有任何线索可以做到这一点,那将是非常好的。

【问题讨论】:

  • 我已经测试了运行 testng-failed.xml 3 次,然后所有的测试都在工作,并且不需要很多时间。但是对于 Jenkins,当最后一次运行 testng-failed.xml 时,这将编辑 testng-result.xml,所以现在图表显示“1 次测试运行,1 次成功”,因为最后一次运行,testng 只启动了这个测试第一次失败了 3 次。此方法将生成一个包含所有失败测试的图表,但不会指示所有工作测试(第三次运行的测试除外),这不是我想要的……有什么线索吗?

标签: java testing selenium jenkins testng


【解决方案1】:

您可以结合使用 IRetryAnalyzer、侦听器和自定义报告器来执行您要查找的操作。

IRetryAnalyzer:

public class RetryAnalyzer implements IRetryAnalyzer  { 
private int count = 0; 
// this number is actually twice the number
// of retry attempts will allow due to the retry
// method being called twice for each retry
private int maxCount = 6; 
protected Logger log;
private static Logger testbaseLog;

static {
    PropertyConfigurator.configure("test-config/log4j.properties");
    testbaseLog = Logger.getLogger("testbase.testng");
}

public RetryAnalyzer()
{
    testbaseLog.trace( " ModeledRetryAnalyzer constructor " + this.getClass().getName() );
    log = Logger.getLogger("transcript.test");
}

@Override 
public boolean retry(ITestResult result) { 
    testbaseLog.trace("running retry logic for  '" 
            + result.getName() 
            + "' on class " + this.getClass().getName() );
        if(count < maxCount) {                     
                count++;                                    
                return true; 
        } 
        return false; 
}
}

重试监听器:

public class RetryTestListener extends TestListenerAdapter  {
private int count = 0; 
private int maxCount = 3; 

@Override
public void onTestFailure(ITestResult result) {     
    Logger log = Logger.getLogger("transcript.test");
    Reporter.setCurrentTestResult(result);

    if(result.getMethod().getRetryAnalyzer().retry(result)) {    
        count++;
        result.setStatus(ITestResult.SKIP);
        log.warn("Error in " + result.getName() + " with status " 
                + result.getStatus()+ " Retrying " + count + " of 3 times");
        log.info("Setting test run attempt status to Skipped");                 
    } 
    else
    {
        count = 0;
        log.error("Retry limit exceeded for " + result.getName());
    }       

    Reporter.setCurrentTestResult(null);
}

@Override
public void onTestSuccess(ITestResult result)
{
    count = 0;
}

但是,TestNG 中似乎存在一个错误,实际上导致某些测试结果报告为既跳过又失败。为防止这种情况,我建议您覆盖您希望使用的任何 Reporter,并包含如下方法:

private IResultMap removeIncorrectlyFailedTests(ITestContext test)
{     
  List<ITestNGMethod> failsToRemove = new ArrayList<ITestNGMethod>();
  IResultMap returnValue = test.getFailedTests();

  for(ITestResult result : test.getFailedTests().getAllResults())
  {
    long failedResultTime = result.getEndMillis();          

    for(ITestResult resultToCheck : test.getSkippedTests().getAllResults())
    {
        if(failedResultTime == resultToCheck.getEndMillis())
        {
            failsToRemove.add(resultToCheck.getMethod());
            break;
        }
    }

    for(ITestResult resultToCheck : test.getPassedTests().getAllResults())
    {
        if(failedResultTime == resultToCheck.getEndMillis())
        {
            failsToRemove.add(resultToCheck.getMethod());
            break;
        }
    }           
  }

  for(ITestNGMethod method : failsToRemove)
  {
      returnValue.removeResult(method);
  }  

  return returnValue;
}

完成所有这些后,您可以使用 .addListener 添加报告器,并在 @Test 注释中指定 retryAnalyzer。

【讨论】:

  • 我们需要TestListenerAdapter吗?
【解决方案2】:

您无需实现 onTestFailure。当测试失败时,TestNG 会自动调用重试。所以不需要重写onTestFailure。这会导致重试方法 2 调用。 我实现了如下重试。


private final Map rerunCountForTesCase = new HashMap();
    @Override
    public boolean retry(ITestResult result) {
                // here i have unique test case IDs for each of test method. 
                // So using utility method to get it. You can use method calss+name combination instead of testcaseid like me
        String executingTestCaseID = ReportingUtilities.getTestCaseId(result);
        if(rerunCountForTesCase.containsKey(executingTestCaseID))
        {
            count = rerunCountForTesCase.get(executingTestCaseID);
        }
        else
        {
            rerunCountForTesCase.put(executingTestCaseID, 0);
        }
        if (count 0)
                {
                    logInfo(tcID,"Sleeping for "+timeInSecs+ " secs before rerunning the testcase");
                    Thread.sleep(timeInSecs * CommonFwBase.SHORTWAIT );
                }
            } catch (InterruptedException e) {
                logError(null, e.getMessage());

            }

            rerunCountForTesCase.put(executingTestCaseID, ++count);
            return true;
        }
        return false;

    }

在上面的线程中,由于 onTestFailure 的实现,重试被调用了两次。我删除了失败的结果,以便在您重试时使用最新结果。否则,如果您有依赖项测试方法,它会被跳过(尽管在重试时它通过了,因为它使用了第一个结果)。您可能必须在报告时处理失败的重试结果。 您可能必须像这样重试后删除正在通过的测试。

        m_ptests = suiteTestContext.getPassedTests();
        m_ftests = suiteTestContext.getFailedTests();
        m_stests = suiteTestContext.getSkippedTests();

        List<ITestNGMethod> methodsToRemove = new ArrayList<ITestNGMethod>();

        for(ITestResult failed_result : m_ftests.getAllResults())
        {
            String failed_testName = failed_result.getMethod().getMethodName();
            String failingTest_className = failed_result.getClass().getName();
            for(ITestResult passed_result : m_ptests.getAllResults())
            {
                String passing_testName = passed_result.getMethod().getMethodName();
                String passingTest_className = failed_result.getClass().getName();
                if(failed_testName.equals(passing_testName) &&  
                        passingTest_className.equals(failingTest_className) ))
                {
                    if(passed_result.getEndMillis() > failed_result.getEndMillis())
                    {

                        methodsToRemove.add(failed_result.getMethod());
                        break;
                    }

                }
            }
        }

        // remove the test that passed on retry
        for(ITestNGMethod failedMethodToRemove : methodsToRemove)
        {
            m_ftests.removeResult(failedMethodToRemove);
        }

希望它有助于更​​好地理解重试。

【讨论】:

    【解决方案3】:

    其他答案是“正确”的方式。对于“quick'n'dirty”方式,只需将实际测试代码移动到私有方法即可。在带注释的测试方法中,创建一个带有 try..catch 的 for 循环,在其中查找 Throwable(所有错误和异常的超类)。出错时继续循环或抛出最后一个错误,成功时中断循环。

    示例代码:

    @Test
    public void testA() throws Throwable {
        Throwable err = null;
        for (int i=0; i<4; i++) {
            try {
                testImplementationA();
                return;
            } catch (Throwable e) {
                err = e;
                continue;
            }
        }
        throw err;
    }
    
    private void testImplementationA()
    {
        // put test code here
    }
    

    不过,通常情况下,最好编写不会随机失败的测试。同样使用这种方法,您不知道有多少次尝试失败,这毕竟是一个重要信息。我个人宁愿在 Jenkins 失败时重新运行整个测试类/套件,并调查失败的原因。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多