如何优化testng和seleniums测试

对于我的实习,我必须使用TestNG和selenium来测试网络应用程序。 但我有一个问题,有时候selenium或浏览器因某些随机原因无法工作,因此工作测试被标记为“失败”。 为了避免这种情况,我可以使用注释@Test(invocationCount = 4, successPercentage = 25) ,然后如果测试成功一次,测试被标记为“Succeed”,那很好,但问题是这个解决方案会倍增时间对于4的测试,这不是很有效。

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

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

所以如果你有任何关于如何做到的线索,那将是非常好的。

您可以结合使用IRetryAnalyzer,Listener和自定义记者来完成您所需要的工作。

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; } } 

RetryListener:

 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 failsToRemove = new ArrayList(); 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。

您无需实现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 methodsToRemove = new ArrayList(); 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); } 

希望有助于更好地了解重试。

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

示例代码:

 @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上重新运行整个测试类/套件失败,并调查它失败的原因。