Réexécuter la classe complète et pas seulement @Test dans TestNG


9

Je parcours stackoverflow depuis quelques jours, essayant de trouver comment réexécuter une classe de test entière, et pas seulement une @Testétape. Beaucoup disent que cela n'est pas pris en charge avec TestNG et IRetryAnalyzer, alors que certains ont publié des solutions de contournement, cela ne fonctionne pas vraiment. Quelqu'un a-t-il réussi à le faire? Et juste pour clarifier les raisons de cela, afin d'éviter les réponses qui disent que ce n'est pas pris en charge dans le but: TestNG n'est pas seulement un outil pour les développeurs. Ce qui est également utilisé par les testeurs sw pour les tests e2e. Les tests E2e peuvent avoir des étapes qui dépendent chacune de la précédente. Alors oui, il est valide de réexécuter toute la classe de test, plutôt que simple @Test, ce qui est facilement réalisable via IRetryAnalyzer.

Un exemple de ce que je veux réaliser serait:

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

}

}

Disons que ça testStep_2échoue, je veux relancer class DemoTestet pas seulementtestStep_2


Pouvez-vous nous montrer la solution de contournement qui ne fonctionne pas?
AndiCover

Veuillez modifier votre question, inclure un échantillon et nous montrer vos attentes. Cela aiderait beaucoup les autres à vous donner une réponse qui répond à vos attentes.
Krishnan Mahadevan

@AndiCover Liens vers des solutions de contournement qui ne fonctionnent pas (ou sont des solutions de contournement qui détruisent la logique testNG): stackoverflow.com/questions/25781098/… stackoverflow.com/questions/50241880/… stackoverflow.com/questions/53736621/…
gandalf_the_cool

Réponses:


1

D'accord, je sais que vous voulez probablement une propriété facile que vous pouvez spécifier dans votre @BeforeClass ou quelque chose comme ça, mais nous devrons peut-être attendre que cela soit implémenté. Au moins, je ne l'ai pas trouvé non plus.

Ce qui suit est moche comme l'enfer mais je pense qu'il fait le travail, au moins à petite échelle, il reste à voir comment il se comporte dans des scénarios plus complexes. Peut-être qu'avec plus de temps, cela peut être amélioré en quelque chose de mieux.

D'accord, j'ai donc créé une classe de test similaire à la vôtre:

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

    }


J'ai le Listenerdans la super classe juste au cas où j'aimerais l'étendre à d'autres classes, mais vous pouvez également définir l'auditeur dans votre classe de test.

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


Trois des 4 méthodes ci-dessus ont un RetryAnalyzer. J'ai laissé le testStep_4sans pour m'assurer que ce que je fais ensuite ne gâche pas le reste de l'exécution. Said RetryAnalyzerne réessayera pas réellement (notez que la méthode retourne false), mais il fera ce qui suit:

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

}


Cela créera une exécution à l'intérieur de votre exécution. Il ne gâchera pas le rapport et, une fois terminé, il continuera avec votre exécution principale. Mais il "réessayera" les méthodes de ce groupe.

Oui, je sais, je sais. Cela signifie que vous allez exécuter votre suite de tests pour toujours dans une boucle éternelle. Voilà pourquoi le RetryAnnotationTransformer. Dans ce document, nous allons supprimer le RetryAnalyzer de la deuxième exécution de ces tests:

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

}


Nous avons maintenant le dernier de nos problèmes. Notre suite de tests d'origine n'y connaît rien à cette exécution de «nouvelle tentative». C'est là que ça devient vraiment moche. Nous devons dire à notre journaliste ce qui vient de se passer. Et c'est la partie que je vous encourage à améliorer. Je manque de temps pour faire quelque chose de plus agréable, mais si je le peux, je le modifierai à un moment donné.

Tout d'abord, nous devons savoir si l'exécution de retryTestNG a réussi. Il y a probablement un million de façons de faire mieux, mais pour l'instant cela fonctionne. J'ai configuré un écouteur uniquement pour l'exécution de la nouvelle tentative. Vous pouvez le voir ci- TestRetrydessus, et il se compose des éléments suivants:

public class TestListenerRetry extends TestConfig implements ITestListener {

    (...)

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

}

Maintenant, l'écouteur de la suite principale, celui que vous avez vu ci-dessus dans la super classe TestConfigverra si l'exécution s'est déroulée et si cela s'est bien passé et mettra à jour le rapport:

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

                }

            }

        }


    }

}

Le rapport devrait montrer maintenant 3 tests réussis (car ils ont été retentés) et un qui a échoué car il ne faisait pas partie des 3 autres tests:

Rapport final


Je sais que ce n'est pas ce que vous recherchez, mais je vous aide à vous servir jusqu'à ce qu'ils ajoutent la fonctionnalité à TestNG.


Oups ... J'ai oublié d'ajouter un conditionnel dans le Listener principal pour ne mettre à jour le rapport final que si la suite Retry s'est produite et qu'elle a réussi. Ajouté maintenant.
Rodrigo Vaamonde
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.