TestMethodOrder : un coup de pouce à utiliser avec précaution

JUnit et son ordre fantasque

Dans certains contextes techniques, nous pouvons choisir simplement l’ordre d’exécution des tests automatisés. Par exemple, Test Batch Runner permet de lancer une suite de tests UFT selon l’ordre indiqué dans le fichier mtb (modular test batch) associé.

En revanche, quand on utilise JUnit, par défaut, ce n’est pas le cas. Le nom des méthodes de test sont hashées, puis les tests sont lancés par ordre alphabétique de hash. Nous nous retrouvons donc à chaque fois avec le même ordre d’exécution, mais à première vue celui-ci ne rime à rien. Ça peut même être assez agaçant au début quand on découvre l’outil !

Un test d’indépendance

Si vos tests autos respectent une des bonnes pratiques capitales en la matière, à savoir qu’ils sont tous indépendants les uns des autres, cela ne devrait cependant pas vous poser problème. En principe, ils devraient pouvoir être joués dans n’importe quel ordre.

Le fait que JUnit lance les tests dans cet ordre particulier peut donc être apprécié, et utilisé comme une manière de vérifier que les tests sont bien indépendants les uns des autres. Du coup, pourquoi ne pas aller encore plus loin ?

Soyons encore plus fous

Pour aller au bout de la logique, nous pourrions décider de jouer les tests dans n’importe quel ordre, cet ordre pouvant changer à chaque exécution. Pour cela, nous allons nous baser non plus sur le hash des noms de méthodes, mais sur l’ordre d’exécution de la JVM, qui n’est pas fixe. Pour ce faire, ajouter simplement « @FixMethodOrder(MethodSorters.JVM) » juste au-dessus de votre classe de test. Cette solution est compatible avec JUnit 4.

@FixMethodOrder(MethodSorters.JVM)
public class TestExemple {

    @Test
    public void test001(){
        System.out.println("1");
    }

    @Test
    public void test002(){
        System.out.println("2");
    }

    @Test
    public void test003(){
        System.out.println("3");
    }

}

Vos tests se joueront maintenant dans n’importe quel ordre, ce qui vous permettra de prouver qu’ils sont vraiment indépendants.

Un ordre fixe

Maintenant, il peut arriver que vous ayez besoin de lancer les tests dans un certain ordre. Si tel est le cas, il suffit simplement de remplacer « MethodSorters.JVM » par « MethodSorters. NAME_ASCENDING », et de ranger les tests par ordre alphabétique en fonction de l’ordre voulu. Cette solution est compatible avec JUnit 4.

D’autres méthodes sont arrivées avec JUnit 5. Par exemple, ceci affichera 1, 2, puis 3, en se basant sur les annotations @Order :

@TestMethodOrder(MethodOrderer.OrderAnnotation.class)

public class TestExemple {

    @Test
    @Order(2)
    public void ccc(){
        System.out.println("2");
    }

    @Test
    @Order(1)
    public void bbb(){
        System.out.println("1");
    }

    @Test
    @Order(3)
    public void aaa(){
        System.out.println("3");
    }
}

Pour conserver un ordre alphabétique et ne pas s’encombrer d’annotations, la syntaxe suivante peut être suivie :

@TestMethodOrder(MethodOrderer.Alphanumeric.class)

public class TestTmp {

    @Test
    public void bbb(){
        System.out.println("2");
    }

    @Test
    public void ccc(){
        System.out.println("3");
    }

    @Test
    public void aaa(){
        System.out.println("1");
    }

}

Celle-ci affichera 1, 2, puis 3.

Quelle que soit la notation choisie, si vous optez pour cette logique, pesez bien le pour et le contre avant de vous lancer.

Exemple de mauvaise utilisation

Une rangée de dominos peut tenir debout pendant des années. Mais il suffit que l’inscription de Boniface07 se déroule avec un petit accroc pour que tous les tests de cette classe se retrouvent en échec.

FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TestExemple extends MaSuperAppliWebTest {

    @Test
    public void test001_inscriptionUtilisateur(){
        accesURL("https://masuperappliweb.com/inscription");
        inscription("Boniface07", "password");
        verifierMessageConfirmationInscription();
    }

    @Test
    public void test002_premiereConnexionUtilisateur(){
        accesURL("https://masuperappliweb.com/connexion");
        connexion("Boniface07", "password");
        verifierPageAccueilAffichee();
    }

    @Test
    public void test003_configurationCompte(){
        accesURL("https://masuperappliweb.com/connexion");
        connexion("Boniface07", "password");
        configurerCompte();
        verifierCompteBienConfigure();
    }

}

Un exemple de motif recevable

Le test X a besoin d’un jeu de données. Celui-ci peut être rapidement créé via une IHM, mais un traitement en back-office durant environ 15 minutes doit être effectué. Nous n’avons pas envie de gaspiller 15 précieuses minutes à attendre la fin de ce traitement ! Nous lançons donc cette création de données au début, nous jouons une flopée de tests ensuite, et seulement à la fin, le test devant utiliser le jeu de données. Des commentaires clairs font état de cette dépendance exceptionnelle. On pourrait même dire qu’on est en présence d’un seul scénario coupé en deux méthodes de test.

Ce n’est pas le seul motif recevable, mais cet exemple montre que cela doit rester exceptionnel et justifié.

A bientôt et bonne automatisation des tests !