Re-executando a classe completa e não apenas o @Test no TestNG

9

Estou navegando no stackoverflow há alguns dias, tentando descobrir como executar novamente uma classe de teste inteira, e não apenas uma @Testetapa. Muitos dizem que isso não é suportado pelo TestNG e IRetryAnalyzer, embora alguns tenham postado soluções alternativas, isso realmente não funciona. Alguém conseguiu fazer isso? E apenas para esclarecer as razões para isso, a fim de evitar respostas que digam que não são suportadas de propósito: TestNG é uma ferramenta não apenas para desenvolvedores. O que também é usado nos testadores sw para o e2e. Os testes E2e podem ter etapas que dependem cada uma das anteriores. Portanto, sim, é válido executar novamente toda a classe de teste, em vez de simples @Test, o que pode ser feito facilmente via IRetryAnalyzer.

Um exemplo do que eu quero alcançar seria:

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"));

}

}

Digamos que testStep_2falha, eu quero executar novamente class DemoTeste não apenastestStep_2

gandalf_the_cool
fonte
Você pode nos mostrar a solução alternativa que não funciona?
AndiCover 6/12/19
Edite sua pergunta, inclua uma amostra e mostre quais são suas expectativas. Isso ajudaria muito os outros a responder a suas expectativas.
Krishnan Mahadevan
@AndiCover Links para soluções alternativas que não funcionam (ou que destroem a lógica do testNG): stackoverflow.com/questions/25781098/… stackoverflow.com/questions/50241880/… stackoverflow.com/questions/53736621/…
gandalf_the_cool

Respostas:

1

Ok, eu sei que você provavelmente deseja uma propriedade fácil que possa ser especificada na sua @BeforeClass ou algo parecido, mas talvez seja necessário aguardar a implementação. Pelo menos eu também não consegui encontrar.

A seguir, é feio como o inferno, mas acho que faz o trabalho, pelo menos em pequena escala, resta ver como ele se comporta em cenários mais complexos. Talvez com mais tempo, isso possa ser aprimorado para algo melhor.

Ok, então eu criei uma classe de teste semelhante à sua:

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.");
        }

    }


Eu tenho a Listenerclasse super apenas no caso em que gostaria de estender isso para outras classes, mas você também pode definir o ouvinte em sua classe de teste.

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


Três dos 4 métodos acima têm a RetryAnalyzer. Deixei o testStep_4sem para ter certeza de que o que estou fazendo a seguir não mexa com o resto da execução. Dito RetryAnalyzer, na verdade, não tentará novamente (observe que o método retornará false), mas fará o seguinte:

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;
    }

}


Isso criará uma execução dentro da sua execução. Não mexerá no relatório e, assim que terminar, continuará com sua execução principal. Mas ele "tentará" novamente os métodos dentro desse grupo.

Sim, eu sei, eu sei. Isso significa que você executará seu conjunto de testes para sempre em um loop eterno. É por isso que o RetryAnnotationTransformer. Nele, removeremos o RetryAnalyzer da segunda execução desses testes:

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);
    }

}


Agora temos o último dos nossos problemas. Nosso conjunto de testes original não sabe nada sobre a "repetição" de execução lá. É aqui que fica realmente feio. Precisamos contar ao nosso repórter o que aconteceu. E esta é a parte que eu encorajo você a melhorar. Falta-me tempo para fazer algo melhor, mas, se puder, vou editá-lo em algum momento.

Primeiro, precisamos saber se a execução retryTestNG foi bem-sucedida. Provavelmente existem milhões de maneiras de fazer isso melhor, mas por enquanto isso funciona. Eu configurei um ouvinte apenas para a nova tentativa de execução. Você pode vê-lo TestRetryacima e consiste no seguinte:

public class TestListenerRetry extends TestConfig implements ITestListener {

    (...)

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

}

Agora, o Ouvinte da suíte principal, o que você viu acima na superclasse TestConfigverá se a execução aconteceu e se correu bem e atualizará o relatório:

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());
                    }

                }

            }

        }


    }

}

O relatório deve mostrar agora três testes aprovados (à medida que foram repetidos) e um que falhou porque não fazia parte dos outros três testes:

Relatório final


Sei que não é o que você está procurando, mas ajudo a atendê-lo até que eles adicionem a funcionalidade ao TestNG.

Rodrigo Vaamonde
fonte
Ops .. Esqueci de adicionar um condicional no Ouvinte principal para atualizar apenas o relatório final se o pacote Repetir acontecer e tiver sido bem-sucedido. Adicionado agora.
Rodrigo Vaamonde