#java #testing #selenium #jenkins #testng
#java #тестирование #селен #дженкинс #testng
Вопрос:
Для моей стажировки я должен использовать TestNG и selenium для тестирования веб-приложения. Но у меня проблема, иногда selenium или браузер не работают по какой-то случайной причине, поэтому рабочий тест помечается как «сбой». Чтобы избежать этого, я могу использовать аннотацию @Test(invocationCount = 4, successPercentage = 25)
, тогда, если тест завершается успешно один раз, тест помечается как «Успешный», это хорошо, но проблема в том, что это решение умножает время тестирования на 4, это не очень эффективно.
Что я могу сделать, чтобы сократить время тестирования, так это написать некоторое правило «если тест не удался, повторно запустите этот тест (и только если тест не удался), и если он сработал во второй, третий или четвертый раз, затем пометьте этот тест как «успешный» «, поэтому яможно избежать этих случайных ошибок. Но я не нашел, как написать это правило, я видел, что мы можем добавить слушателя, поэтому у нас есть метод с именем « onTestFailure
«, поэтому я могу что-то сделать, когда тест не удался, но я не знаю, как повторно запустить тест.
Я также нашел testng-failed.xml где сохраняются все неудачные тесты, поэтому мы можем запустить этот XML-файл для повторного запуска этих тестов, но это приведет к удалению отчета из предыдущего первого запуска, но я просто хочу, чтобы неудачные тесты были помечены как «успешные», если второй запуск будет успешным. (Я интегрировал TestNG / selenium в Jenkins, поэтому у меня есть график со всеми тестами, поэтому этот метод не очень адаптирован, но этот метод не умножает время тестирования на 4, и это то, что я хочу)
Так что, если у вас есть какие-либо подсказки о том, как это сделать, было бы очень приятно.
Комментарии:
1. Я протестировал для запуска testng-failed.xml 3 раза, и тогда все тесты работают, и это не занимает много времени. Но с Дженкинсом, когда testng-failed.xml выполняется в последний раз, это приведет к редактированию testng-result.xml , так что теперь на графике указано «1 тестовый запуск, 1 успешный», потому что при последнем запуске testng запустил только этот тест, который не прошел 3 первых раза. Этот метод создаст график со всеми неудачными тестами, но все рабочие тесты (кроме тестов, которые выполняются в третий раз) не будут указаны, это не совсем то, что я хочу… есть какие-нибудь подсказки?
Ответ №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;
}
}
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<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;
}
После того, как все это будет сделано, вы можете добавить reporter с помощью using .addListener и указать retryAnalyzer в аннотации @Test .
Комментарии:
1. Нужен ли нам 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) amp;amp;
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:
Другие ответы являются «правильным» способом. Для «быстрого и грязного» способа просто переместите фактический тестовый код в частный метод. В вашем аннотированном методе тестирования создайте цикл for с помощью try..catch внутри, где вы ищете 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 при сбое и выяснять, почему он не удался.