如何优化测试和硒测试

时间:2011-10-18 07:16:30

标签: java testing selenium jenkins testng

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

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

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

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

3 个答案:

答案 0 :(得分: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中似乎存在一个错误,实际上会导致某些测试结果被报告为跳过和失败。为了防止这种情况,我建议您覆盖您想要使用的任何报告者,并包括如下所示的方法:

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。

答案 1 :(得分:4)

您无需实施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);
        }

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

答案 2 :(得分:0)

其他答案是&#34;对&#34;办法。对于&#34;快速&#39;脏&#34;方式只是将实际的测试代码移动到私有方法。在带注释的测试方法中,使用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上重新运行整个测试类/套件失败,并调查它失败的原因。