Повторное выполнение полного класса, а не только @Test в TestNG

9

Я просматривал stackoverflow в течение нескольких дней, пытаясь найти способ перезапустить целый тестовый класс, а не просто @Testшаг. Многие говорят, что это не поддерживается с TestNG и IRetryAnalyzer, хотя некоторые опубликовали обходные пути, которые на самом деле не работают. Кому-нибудь удалось это сделать? И просто чтобы выяснить причины этого, чтобы избежать ответов, которые говорят, что не поддерживается в целях: TestNG - инструмент не только для разработчиков. Это означает, что также используется тестерами SW для тестирования e2e. Тесты E2e могут иметь этапы, каждый из которых зависит от предыдущего. Так что да, допустимо перезапустить весь тестовый класс, а не простой @Test, что легко сделать с помощью IRetryAnalyzer.

Пример того, чего я хочу достичь:

public class DemoTest extends TestBase {

@Test(alwaysRun = true, description = "Do this")
public void testStep_1() {
    driver.navigate().to("http://www.stackoverflow.com");
    Assert.assertEquals(driver.getCurrentUrl().contains("stackoverflow)"));

}

@Test(alwaysRun = true, dependsOnMethods = "testStep_1", description = "Do that")
public void testStep_2() {
    driver.press("button");
    Assert.assertEquals(true, driver.elementIsVisible("button"));

}

@Test(alwaysRun = true, dependsOnMethods = "testStep_2", description = "Do something else")
public void testStep_3() {
   driver.press("button2");
Assert.assertEquals(true, driver.elementIsVisible("button"));

}

}

Допустим, что testStep_2не получается, я хочу перезапустить, class DemoTestа не толькоtestStep_2

gandalf_the_cool
источник
Можете ли вы показать нам обходной путь, который не работает?
AndiCover
Пожалуйста, отредактируйте свой вопрос, включите образец и покажите нам, каковы ваши ожидания. Это помогло бы другим дать вам ответ, который соответствует вашим ожиданиям.
Кришнан Махадеван
@AndiCover Ссылки на обходные пути, которые не работают (или обходные пути, которые разрушают логику testNG): stackoverflow.com/questions/25781098/… stackoverflow.com/questions/50241880/… stackoverflow.com/questions/53736621/…
gandalf_the_cool

Ответы:

1

Хорошо, я знаю, что вам, вероятно, нужно какое-то простое свойство, которое вы можете указать в своем @BeforeClass, или что-то подобное, но нам, возможно, придется подождать, пока это не будет реализовано. По крайней мере, я тоже не смог его найти.

Следующее ужасно ужасно, но я думаю, что оно делает свою работу, по крайней мере, в небольшом масштабе, осталось посмотреть, как оно ведет себя в более сложных сценариях. Может быть, со временем это может быть улучшено до чего-то лучшего.

Итак, я создал тестовый класс, похожий на ваш:

public class RetryTest extends TestConfig {

    public class RetryTest extends TestConfig {

        Assertion assertion = new Assertion();

        @Test(  enabled = true,
                groups = { "retryTest" },
                retryAnalyzer = TestRetry.class,
                ignoreMissingDependencies = false)
        public void testStep_1() {
        }

        @Test(  enabled = true,
                groups = { "retryTest" },
                retryAnalyzer = TestRetry.class,
                dependsOnMethods = "testStep_1",
                ignoreMissingDependencies = false)
        public void testStep_2() {
            if (fail) assertion.fail("This will fail the first time and not the second.");
        }

        @Test(  enabled = true,
                groups = { "retryTest" },
                retryAnalyzer = TestRetry.class,
                dependsOnMethods = "testStep_2",
                ignoreMissingDependencies = false)
        public void testStep_3() {
        }

        @Test(  enabled = true)
        public void testStep_4() {
            assertion.fail("This should leave a failure in the end.");
        }

    }


У меня есть Listenerсуперкласс на тот случай, если бы я хотел расширить его на другие классы, но вы также можете установить слушателя в своем тестовом классе.

@Listeners(TestListener.class)
public class TestConfig {
   protected static boolean retrySuccessful = false;
   protected static boolean fail = true;
}


Три из 4 методов выше имеют RetryAnalyzer. Я оставил testStep_4без него, чтобы убедиться, что то, что я делаю дальше, не мешает остальной части исполнения. Сказанный RetryAnalyzerне будет на самом деле повторять (обратите внимание, что метод возвращает false), но он сделает следующее:

public class TestRetry implements IRetryAnalyzer {

    public static TestNG retryTestNG = null;

    @Override
    public boolean retry(ITestResult result) {
        Class[] classes = {CreateBookingTest.class};

        TestNG retryTestNG = new TestNG();
        retryTestNG.setDefaultTestName("RETRY TEST");
        retryTestNG.setTestClasses(classes);
        retryTestNG.setGroups("retryTest");
        retryTestNG.addListener(new RetryAnnotationTransformer());
        retryTestNG.addListener(new TestListenerRetry());
        retryTestNG.run();

        return false;
    }

}


Это создаст исполнение внутри вашего исполнения. Он не будет связываться с отчетом, и как только он закончится, он продолжит ваше основное выполнение. Но он «попробует» методы в этой группе.

Да, я знаю, я знаю. Это означает, что вы будете выполнять свой набор тестов вечно в вечном цикле. Вот почему RetryAnnotationTransformer. В нем мы удалим RetryAnalyzer из второго выполнения этих тестов:

public class RetryAnnotationTransformer extends TestConfig implements IAnnotationTransformer {

    @SuppressWarnings("rawtypes")
    @Override
    public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
        fail = false; // This is just for debugging. Will make testStep_2 pass in the second run.
        annotation.setRetryAnalyzer(null);
    }

}


Теперь у нас есть последние из наших проблем. Наш оригинальный набор тестов ничего не знает об этом «повторном» выполнении. Вот где это становится действительно ужасно. Нам нужно рассказать нашему репортеру, что только что произошло. И это та часть, которую я призываю вас улучшить. У меня не хватает времени, чтобы сделать что-то более приятное, но если я смогу, я отредактирую это в какой-то момент.

Во-первых, нам нужно знать, было ли выполнение retryTestNG успешным. Вероятно, есть миллион способов сделать это лучше, но пока это работает. Я настроил слушателя только для повторного выполнения. Вы можете видеть это TestRetryвыше, и это состоит из следующего:

public class TestListenerRetry extends TestConfig implements ITestListener {

    (...)

    @Override
    public void onFinish(ITestContext context) {
        if (context.getFailedTests().size()==0 && context.getSkippedTests().size()==0) {
            successful = true;
        }
    }

}

Теперь слушатель основного набора, который вы видели выше в суперклассе TestConfig, увидит, произошел ли запуск, и прошел ли он успешно, и обновит отчет:

public class TestListener extends TestConfig implements ITestListener , ISuiteListener {

    (...)

    @Override
    public void onFinish(ISuite suite) {

        if (TestRetry.retryTestNG != null) {

            for (ITestNGMethod iTestNGMethod : suite.getMethodsByGroups().get("retryTest")) {

                Collection<ISuiteResult> iSuiteResultList = suite.getResults().values();

                for (ISuiteResult iSuiteResult : iSuiteResultList) {

                    ITestContext iTestContext = iSuiteResult.getTestContext();
                    List<ITestResult> unsuccessfulMethods = new ArrayList<ITestResult>();

                    for (ITestResult iTestResult : iTestContext.getFailedTests().getAllResults()) {
                        if (iTestResult.getMethod().equals(iTestNGMethod)) {
                            iTestContext.getFailedTests().removeResult(iTestResult);
                            unsuccessfulMethods.add(iTestResult);
                        }
                    }

                    for (ITestResult iTestResult : iTestContext.getSkippedTests().getAllResults()) {
                        if (iTestResult.getMethod().equals(iTestNGMethod)) {
                            iTestContext.getSkippedTests().removeResult(iTestResult);
                            unsuccessfulMethods.add(iTestResult);
                        }
                    }

                    for (ITestResult iTestResult : unsuccessfulMethods) {
                        iTestResult.setStatus(1);
                        iTestContext.getPassedTests().addResult(iTestResult, iTestResult.getMethod());
                    }

                }

            }

        }


    }

}

Теперь в отчете должны быть показаны 3 теста (так как они были повторены) и один, который не прошел, потому что он не был частью 3 других тестов:

Заключительный отчет


Я знаю, что это не то, что вы ищете, но помогу вам, пока они не добавят функциональность в TestNG.

Родриго Ваамондэ
источник
К сожалению, я забыл добавить условие в основной прослушиватель, чтобы обновлять окончательный отчет только в том случае, если произошел набор Retry, и он был успешным. Добавлено сейчас.
Родриго