对于我的实习,我必须使用TestNG和selenium来测试网络应用程序。但我有一个问题,有时候selenium或浏览器因某些随机原因无法正常工作,因此工作测试被标记为“失败”。为了避免这种情况,我可以使用注释@Test(invocationCount = 4, successPercentage = 25)
,然后如果测试成功一次,测试被标记为“成功”,这很好,但问题是这个解决方案将测试时间乘以4,这个不是很有效率。
我可以做些什么来减少测试时间,是写一些规则“如果测试失败,重新运行此测试(并且只有在测试失败时),以及它是否在第二,第三或第四时间,然后将此测试标记为“成功”“所以我可以避免这些随机错误。但我还没有找到如何编写这个规则,我看到我们可以添加一个监听器,所以我们有一个名为“onTestFailure
”的方法,所以当测试失败但我不知道时我可以做点什么如何重新运行测试。
我还发现了testng-failed.xml,其中保存了所有失败的测试,因此我们可以运行此xml文件以重新运行这些测试,但这将删除上一次运行的报告,但我只想失败如果第二次运行成功,则测试标记为“成功”。 (我已将testNG / selenium集成到Jenkins中,所以我有一个包含所有测试的图表,所以这个方法不是很适应,但这种方法不会将测试时间乘以4,这就是我想要的)
所以,如果你有任何关于如何做到这一点的线索,那将是非常好的。
答案 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上重新运行整个测试类/套件失败,并调查它失败的原因。