2015-03-17 4 views
5

Как и решение, размещенное здесь TestNG retrying failed tests doesn't output the correct test results, я пытаюсь удалить (дублированный) результат теста с помощью тестового прослушивателя во время onFinish (контекст ITestContext).Удалить (дублировать) сбой Результат TestNG через тестовый прослушиватель

Хотя удаление результата с помощью context.getFailedTests(). RemoveResult (result), похоже, работает нормально (результат фактически удален), похоже, есть «другое место», где результаты вытягиваются из причины сборка все еще терпит неудачу.

Также обратите внимание, что при выполнении тестового теста из вышеприведенной статьи (которая имеет один дублирующий отказ для удаления и один пройденный тест), я получаю разницу в «результатах теста» (не очищается, как ожидалось) против. «результаты набора» (дублирующий отказ удаляется, как ожидалось).

И где отчетность вытягивает результаты, чтобы решить, следует ли отказываться от сборки? Или это просто, что он тянет результаты, прежде чем я очищаю неудачные тесты ...?

=============================================== 
    Default test 
    Tests run: 3, Failures: 2, Skips: 0 
=============================================== 

=============================================== 
Default suite 
Total tests run: 2, Failures: 1, Skips: 0 
=============================================== 

EDIT: Просто для уточнения, мы бежим эти тесты с Maven, и они ITs, поэтому мы выполним их с отказоустойчивой плагин. Проблема состоит в том, что, хотя кажется, что тесты удалены, mvn проверяет, что все еще не удалось построить сборку, поскольку считает, что сбои сборки обнаруживаются независимо.

А также, если вы запускаете такой тест из Eclipse, даже если тесты удалены, в журнале все еще печатаются сбои, когда набор заканчивается.

О RetryAnalyzer: Я бы не стал использовать хорошую/лучшую практику RetryAnalyzer, но если вы окажетесь в ситуации, когда вам нужно решить проблему, например. вы унаследовали набор тестов, который полагается на RetryAnalyzer, вы можете найти это полезным.

+0

Вы нашли решение этой проблемы между ними? – MrSpock

+0

Мы вроде как выяснили что-то «полезное», хотя и не то, что я в конечном счете хотел. Я попытаюсь найти некоторое время в течение следующих нескольких дней, чтобы очистить код и опубликовать его. – mac

+0

Желаем увидеть ваше решение! – MrSpock

ответ

2

Попробуйте использовать этот код:

ListenerApadter:

public class MyTestListenerAdapter extends TestListenerAdapter { 
    @Override 
    public void onTestFailure(ITestResult result) { 
     if (result.getMethod().getRetryAnalyzer() != null) { 
      MyRetryAnalyzer retryAnalyzer = (MyRetryAnalyzer)result.getMethod().getRetryAnalyzer(); 

      if(retryAnalyzer.isRetryAvailable()) { 
       result.setStatus(ITestResult.SKIP); 
      } else { 
       result.setStatus(ITestResult.FAILURE); 
      } 
      Reporter.setCurrentTestResult(result); 
     } 
    } 

    @Overrride 
    public void onFinish(ITestContext context) { 
    Iterator<ITestResult> failedTestCases =context.getFailedTests().getAllResults().iterator(); 
    while (failedTestCases.hasNext()) { 
     System.out.println("failedTestCases"); 
     ITestResult failedTestCase = failedTestCases.next(); 
     ITestNGMethod method = failedTestCase.getMethod(); 
     if (context.getFailedTests().getResults(method).size() > 1) { 
      System.out.println("failed test case remove as dup:" + failedTestCase.getTestClass().toString()); 
      failedTestCases.remove(); 
     } else { 

      if (context.getPassedTests().getResults(method).size() > 0) { 
       System.out.println("failed test case remove as pass retry:" + failedTestCase.getTestClass().toString()); 
       failedTestCases.remove(); 
      } 
     } 
    } 
    } 
} 

RetryAnalizer:

public class MyRetryAnalyzer implements IRetryAnalyzer { 
    private static int MAX_RETRY_COUNT = 3; 

    AtomicInteger count = new AtomicInteger(MAX_RETRY_COUNT); 

    public boolean isRetryAvailable() { 
     return (count.intValue() > 0); 
    } 

    @Override 
    public boolean retry(ITestResult result) { 
     boolean retry = false; 
     if (isRetryAvailable()) { 
      System.out.println("Going to retry test case: " + result.getMethod() + ", " + (MAX_RETRY_COUNT - count.intValue() + 1) + " out of " + MAX_RETRY_COUNT); 
      retry = true; 
      count.decrementAndGet(); 
     } 
     return retry; 
    } 
} 

pom.xml -> Surefire Конфигурация:

Здесь вы должны настроить «перезаписать» верный слушатель, у которого есть свои счетчики.

<plugin> 
    <groupId>org.apache.maven.plugins</groupId> 
    <artifactId>maven-surefire-plugin</artifactId> 
    <version>2.18.1</version> 
    <configuration> 
    <suiteXmlFiles><suiteXmlFile>${basedir}/testng.xml</suiteXmlFile></suiteXmlFiles> 
<properties> 
    <property> 
    <name>listener</name> 
    <value>Utils.MyTestListenerAdapter,Utils.MyRetryAnalizer</value> 
    </property> 
</properties> 

+0

Спасибо, но я боюсь, что это не будет работать надежно. Прежде всего, мы рассматриваем тестовые прогоны с проскальзыванием как неуспешные. Во-вторых, решение кажется немного рискованным, поскольку не каждый RetryAnalyzer может быть такого типа (возможно, в небольших проектах, но не в больших проектах, охватывающих несколько проектов maven). Однако идея с Reporter.setCurrentStatus() кажется интересной. Кроме того, на стороне примечание: этот тип решения RetryAnalyzer не работает должным образом для методов тестирования с параметрами/поставщиками данных. – mac

+0

@mac Обновлено: добавьте код в «onFinish», чтобы обработать способ, которым вам нужны пропущенные и неудачные тесты. – Morvader

+0

в целом, я думаю, что у вас есть некоторые противоречия в вашем коде, поскольку вы отмечаете тест как пропустить, повторное удаление сбоев позже. Кроме того, я думаю, вы могли бы просто переместить всю часть, которая у вас есть в onTestFailure(), в сам RetryAnalyzer, и вам также не придется делать рискованное кастинг. В общем, я думаю, что это все тот же подход, что и в ссылке, которую я разместил наверху. Дело в том, что, когда вы все это делаете, я считаю, что фаза проверки mvn (потому что мы на самом деле делаем это для ИТ-специалистов с отказоустойчивым не уверенным), все равно не сработает сборка – mac

0

Я заканчивал тем, что решение, которое использует люкс слушателя.

Решение может не полностью очистить то, что TestNG/ваши тесты регистрируются на консоли, но если вы используете плагин TestNG Jenkins и сначала имели сбой при каждом тестировании, а затем успех, тестовый запуск заканчивается зеленый, который, я думаю, является самым важным.

И да, мы запускаем mvn integration-test (не проверяем mvn), и пусть плагин TestNG имеет дело с пропуском/сбоем. The solution is quite similar/builds on what was posted here.

import java.util.Map; 

import org.apache.logging.log4j.LogManager; 
import org.apache.logging.log4j.Logger; 
import org.testng.ISuite; 
import org.testng.ISuiteListener; 
import org.testng.ISuiteResult; 
import org.testng.ITestContext; 

/** 
* {@link ISuiteListener} implementation to clean up duplicate test results caused by retrying tests using the 
* {@link RetryAnalyzer} 
*/ 
public class SuiteResultListener implements ISuiteListener { 

    private static final Logger LOG = LogManager.getLogger(); 

    @Override 
    public void onStart(ISuite suite) { 
    } 

    @Override 
    public void onFinish(ISuite suite) { 
     LOG.info("Cleaning up duplicate test failures in suite '" + suite.getName() + "' ..."); 
     final Map<String, ISuiteResult> results = suite.getResults(); 
     int removedFailures = 0; 
     for (ISuiteResult result : results.values()) { 
      final ITestContext testContext = result.getTestContext(); 

      removedFailures += TestListenerUtil.cleanUpDuplicateFailures(testContext); 
     } 

     LOG.info("Removed " + removedFailures + " duplicate test failure(s) from suite '" + suite.getName() + "'"); 
    } 
} 

И вот волшебство, которое происходит в классе TestListenerUtil:

public static int cleanUpDuplicateFailures(ITestContext testContext) { 
    final String testContextName = testContext.getName(); 
    int removedFailures = 0; 

    LOG.info("Cleaning up failures in test context '" + testContextName + "' ..."); 
    final Set<ITestResult> failedTests = testContext.getFailedTests().getAllResults(); 
    if (failedTests.isEmpty()) { 
     LOG.info("There are no failures in test context '" + testContextName + "'\n"); 
    } else { 
     // collect all id's from passed test 
     final Set<Integer> passedTestIds = new HashSet<>(); 
     final Set<ITestResult> passedTests = testContext.getPassedTests().getAllResults(); 
     LOG.info("Analyzing " + passedTests.size() + " passed test(s)"); 
     for (ITestResult result : passedTests) { 
      final int testId = TestListenerUtil.getId(result); 
      passedTestIds.add(testId); 
      LOG.info(" Passed test " + TestListenerUtil.getName(result) + ": #" + testId + " @ " 
        + getStartTime(result)); 
     } 

     // check which failed test results should be removed 
     final List<Integer> resultsToBeRemoved = new ArrayList<>(); 
     final Set<Integer> failedTestIds = new HashSet<>(); 

     LOG.info("Analyzing " + failedTests.size() + " failed test(s)"); 
     for (ITestResult result : failedTests) { 
      final int testId = TestListenerUtil.getId(result); 
      final String name = TestListenerUtil.getName(result); 

      // if we saw this test pass or fail before we mark the result for deletion 
      if (failedTestIds.contains(testId) || passedTestIds.contains(testId)) { 
       LOG.info(" Adding test " + name + " to be removed: #" + testId + " @ " + getStartTime(result)); 
       resultsToBeRemoved.add(testId); 
      } else { 
       LOG.info(" Remembering failed test " + name + ": #" + testId + " @ " + getStartTime(result)); 
       failedTestIds.add(testId); 
      } 
     } 

     // finally delete all duplicate failures (if any) 
     final int duplicateFailures = resultsToBeRemoved.size(); 
     if (duplicateFailures > 0) { 
      LOG.info("Cleaning up failed tests (expecting to remove " + resultsToBeRemoved.size() 
        + " result(s)) ..."); 
      for (ITestResult result : testContext.getFailedTests().getAllResults()) { 
       final int testId = TestListenerUtil.getId(result); 
       final String info = TestListenerUtil.getName(result) + ": #" + testId + " @ " 
         + getStartTime(result); 
       if (resultsToBeRemoved.contains(testId)) { 
        LOG.info(" Removing failed test result " + info); 
        testContext.getFailedTests().removeResult(result); 
        resultsToBeRemoved.remove((Integer) testId); 
        removedFailures++; 
       } else { 
        LOG.info(" Not removing failed test result " + info); 
       } 
      } 
     } 

     if (removedFailures == duplicateFailures) { 
      LOG.info("Removed " + removedFailures + " failed test result(s) in '" + testContextName + "'\n"); 
     } else { 
      LOG.warn("Removed " + removedFailures + " failed test result(s) in '" + testContextName 
        + "' (expected to remove " + duplicateFailures + ")\n"); 
     } 
    } 

    return removedFailures; 
} 

С этими двумя дополнительными методами Utils:

public static String getName(ITestResult result) { 
    final List<String> parameters = new ArrayList<>(); 
    if (result.getParameters() != null) { 
     for (Object parameter : result.getParameters()) { 
      if (parameter instanceof TestResult && ((TestResult) parameter).getStatus() < 0) { 
       // TestResult.toString() will explode with status < 0, can't use the toString() method 
       parameters.add(parameter.getClass().getName() + "@" + parameter.hashCode()); 
      } else { 
       parameters.add(parameter == null ? "null" : parameter.toString()); 
      } 
     } 
    } 

    return result.getTestClass().getRealClass().getSimpleName() + "." + result.getMethod().getMethodName() + "(" 
      + StringUtils.join(parameters, ",") + ")"; 
} 

public static int getId(ITestResult result) { 
    final HashCodeBuilder builder = new HashCodeBuilder(); 
    builder.append(result.getTestClass().getRealClass()); 
    builder.append(result.getMethod().getMethodName()); 
    builder.append(result.getParameters()); 
    return builder.toHashCode(); 
} 

А также, если вы заинтересованы, как наш RetryAnalyzer работает, см. ниже.

Одна вещь, которая важна для понимания, заключается в том, что мы учитываем параметры метода тестирования как в RetryAnalyzer, так и в результате дублирования результата. Это важно, потому что мы часто работаем с DataProviders.

import java.util.HashMap; 
import java.util.Map; 

import org.apache.logging.log4j.LogManager; 
import org.apache.logging.log4j.Logger; 
import org.testng.IRetryAnalyzer; 
import org.testng.ITestResult; 

public class RetryAnalyzer implements IRetryAnalyzer { 

    private static final Logger LOG = LogManager.getLogger(); 

    private static Integer maxRetries; 
    private final Map<Integer, Integer> retryCount = new HashMap<>(); 

    @Override 
    public boolean retry(ITestResult result) { 
     // only re-try failures 
     if (result.getStatus() == ITestResult.FAILURE) { 
      final String testName = TestListenerUtil.getName(result); 
      final int count = getRetryCount(result); 
      final int maxRetriesAllowed = getMaxRetriesAllowed(); 
      if (count < maxRetriesAllowed) { 
       retryCount.put(TestListenerUtil.getId(result), count + 1); 
       LOG.info("Retrying test (attempt " + (count + 1) + "/" + maxRetriesAllowed + "): " + testName); 
       return true; 
      } else { 
       LOG.error("Failing test after " + count + " retries: " + testName); 
      } 
     } 

     return false; 
    } 

    public boolean canRetry(ITestResult result) { 
     return result.getStatus() == ITestResult.FAILURE && getRetryCount(result) < getMaxRetriesAllowed(); 
    } 

    private int getRetryCount(ITestResult result) { 
     final int testId = TestListenerUtil.getId(result); 
     return retryCount.containsKey(testId) ? retryCount.get(testId) : 0; 
    } 

    public static int getMaxRetriesAllowed() { 
     return maxRetries == null ? Config.MAX_TEST_RETRIES : maxRetries; 
    } 
} 
+0

. Я вернусь к этой проблеме. Просто понял, что testng изменил поведение в 6.9.5! Тесты, которые будут повторены, будут теперь пропущены. См. Https://github.com/cbeust/testng/issues/878. Это сделает любой TestListener, который удалит неудачные тесты устаревшими. – MrSpock

+0

Я не обязательно согласен: я думаю, вы захотите удалить пропущенные. Но, здорово знать, что это изменяется в 6.9.5! Благодарю. – mac

Смежные вопросы