Comment exécuter les méthodes de test dans un ordre spécifique dans JUnit4?

413

Je veux exécuter des méthodes de test qui sont annotées par @Testdans un ordre spécifique.

Par exemple:

public class MyTest {
    @Test public void test1(){}
    @Test public void test2(){}
}

Je veux m'assurer de m'exécuter test1()avant test2()chaque exécution MyTest, mais je n'ai pas trouvé d'annotation comme @Test(order=xx).

Je pense que c'est une fonctionnalité assez importante pour JUnit, si l'auteur de JUnit ne veut pas la fonctionnalité de commande , pourquoi?

卢 声 远 Shengyuan Lu
la source
2
Ils me semblent être exécutés dans l'ordre où ils apparaissent dans le fichier source.
Marquis de Lorne,
84
Vous ne devez jamais écrire de tests qui doivent être exécutés dans un ordre spécifié. C'est vraiment une mauvaise pratique. Chaque test doit pouvoir s'exécuter indépendamment.
Apfelsaft le
5
@EJP, c'était presque universellement vrai pour java pre 7. Avant 7, la plupart des JVM le faisaient, mais ce n'était jamais garanti. Les machines virtuelles Java 7 peuvent renvoyer les méthodes dans un ordre non déterministe.
Matthew Farwell
17
Solution de contournement. Supprimez @Test de vos cas de test, convertissez-les en fonctions privées, puis créez un seul cas de test et appelez des fonctions privées dans l'ordre.
Simon Guo
12
La suppression de @Test des cas de test gâchera le rapport JUnit. Soit dit en passant, l'application d'une ordonnance spécifique est une mauvaise pratique pour les tests unitaires mais pas nécessairement une mauvaise pratique pour les tests d'intégration . Le meilleur choix (pas idéal) est annoter la classe avec @FixMethodOrder(MethodSorters.NAME_ASCENDING), garder l' @Testannotation pour toutes les méthodes de test et les renommer en fonction par ordre alphabétique dans l'ordre souhaité d'exécution, par exemple t1_firstTest(), t2_secondTest()etc.
MisterStrickland

Réponses:

238

Je pense que c'est une fonctionnalité assez importante pour JUnit, si l'auteur de JUnit ne veut pas la fonctionnalité de commande, pourquoi?

Je ne suis pas sûr qu'il existe un moyen propre de le faire avec JUnit, à ma connaissance JUnit suppose que tous les tests peuvent être effectués dans un ordre arbitraire. De la FAQ:

Comment utiliser un appareil d'essai?

(...) L'ordre des appels de méthode de test n'est pas garanti , donc testOneItemCollection () peut être exécuté avant testEmptyCollection (). (...)

Pourquoi en est-il ainsi? Eh bien, je crois que rendre les tests dépendants de l'ordre est une pratique que les auteurs ne veulent pas promouvoir. Les tests doivent être indépendants, ils ne devraient pas être couplés et de violer ce sera rendre les choses plus difficiles à maintenir, briseront la possibilité d'exécuter des tests individuellement (évidemment), etc.

Cela étant dit, si vous voulez vraiment aller dans cette direction, envisagez d'utiliser TestNG car il prend en charge l'exécution native des méthodes de test (et des choses comme spécifier que les méthodes dépendent de groupes de méthodes). Cedric Beust explique comment procéder par ordre d'exécution des tests dans testng .

Pascal Thivent
la source
14
Soit vous avez deux tests indépendants, soit vous n'avez qu'un seul test et vous devez coder comme tel.
Jon Freedman,
2
@ JonFreedman, si je comprends bien la question, il ne s'agit pas que les tests soient interdépendants, juste d'avoir une spécification de choses à tester et de vouloir que les résultats apparaissent dans cet ordre.
Jon Bright
174
Je peux comprendre de ne pas appliquer l'ordre des tests unitaires, mais lors de l'utilisation de JUnit pour écrire des tests d'intégration, il serait bien de pouvoir spécifier l'ordre dans lequel les tests sont exécutés. Par exemple, lancez d'abord le test de connexion.
Brian DiCasa
13
@BrianD. la connexion est probablement un "appareil" au lieu d'un test qui doit s'exécuter avant tous les autres. J'écrirai probablement une BeforeClass qui se connecte, puis j'écrirai les tests à exécuter dans n'importe quel ordre.
marcospereira
47
L'implication "les tests doivent être indépendants => les tests doivent être indépendants de l'ORDRE" n'est pas vraie. Envisagez une notation automatisée des devoirs des élèves. Je veux tester leur solution pour des entrées plus petites en premier et pour des entrées plus grandes plus tard. Lorsque la solution échoue pour des entrées plus petites (pour la limite de temps / mémoire), alors pourquoi les tests devraient-ils s'exécuter pour des entrées plus grandes?
mirelon
96

Si vous vous débarrassez de votre instance existante de Junit et téléchargez JUnit 4.11 ou une version ultérieure dans le chemin de génération, le code suivant exécutera les méthodes de test dans l'ordre de leurs noms, triés par ordre croissant:

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class SampleTest {

    @Test
    public void testAcreate() {
        System.out.println("first");
    }
    @Test
    public void testBupdate() {
        System.out.println("second");
    }
    @Test
    public void testCdelete() {
        System.out.println("third");
    }
}
Aniket Kulkarni
la source
8
Nous avons en fait essayé une méthode similaire test_001_login (), par exemple, mais bien qu'elle fonctionne principalement pour préserver l'ordre, elle n'est pas garantie - nous avons plusieurs instances par test où 004, 005 et 006 sont exécutées après 007. Cela fait vous dites "WTF !," et lancez-vous sur StackOverflow pour obtenir des réponses.
Max P Magee
Génial - disponible dans JUnit 4.12
DtechNet
1
dans mes tests: testAcase - a fonctionné, test_A_case / testA_case - n'a pas fonctionné!
Rodislav Moldovan
6
J'ai essayé ce paramètre d'annotation "MethodSorters.JVM", par exemple "@FixMethodOrder (MethodSorters.JVM)". Depuis l'API: JVM - Laisse les méthodes de test dans l'ordre renvoyé par la JVM. Fonctionne très bien pour ce que je fais (CRUD), exécute les méthodes de test dans l'ordre dans
lequel
1
Cette annotation est en effet une réponse, mais elle a la mise en garde qu'elle n'est pas définie (dans Junit 4.12) avec @Inheritedet devient donc inefficace sur ma AbstractTestCaseclasse parente.
AbVog
49

Si la commande est importante, vous devez la passer vous-même.

@Test public void test1() { ... }
@Test public void test2() { test1(); ... }

En particulier, vous devez répertorier certaines ou toutes les permutations de commande possibles à tester, si nécessaire.

Par exemple,

void test1(); 
void test2(); 
void test3(); 


@Test
public void testOrder1() { test1(); test3(); }

@Test(expected = Exception.class)
public void testOrder2() { test2(); test3(); test1(); }

@Test(expected = NullPointerException.class)
public void testOrder3() { test3(); test1(); test2(); }

Ou, un test complet de toutes les permutations:

@Test
public void testAllOrders() {
    for (Object[] sample: permute(1, 2, 3)) {
        for (Object index: sample) {
            switch (((Integer) index).intValue()) {
                case 1: test1(); break; 
                case 2: test2(); break; 
                case 3: test3(); break; 
            }
        }
    }
}

Voici permute()une fonction simple qui itère toutes les permations possibles dans une collection de tableaux.

Xiè Jìléi
la source
Mais que faire si des tests dans des fichiers différents?
Oleg Abrazhaev
3
Dans le premier bloc de code, test2s'exécute à test1 nouveau . Junit peut encore fonctionner test2avant test1. Ce n'est probablement pas ce que vous vouliez, et ce n'est pas une réponse valable à la question.
toolforger
47

La migration vers TestNG semble la meilleure façon, mais je ne vois pas de solution claire ici pour jUnit. Voici la solution / le formatage le plus lisible que j'ai trouvé pour jUnit:

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class SampleTest {
    @Test
    void stage1_prepareAndTest(){};

    @Test
    void stage2_checkSomething(){};

    @Test
    void stage2_checkSomethingElse(){};

    @Test
    void stage3_thisDependsOnStage2(){};

    @Test
    void callTimeDoesntMatter(){}
}

Cela garantit que les méthodes stage2 sont appelées après celles de stage1 et avant celles de stage3.

joro
la source
5
Cette approche est agréable, mais il serait valable de mentionner que si vous avez plus de 10 tests, cela ne fonctionnera pas 0void stage01_prepareAndTest(){ }
correctement
Si vous avez plus de 10 étapes (pas de tests) - Oui. Je préfère limiter le nombre d'étapes et avoir plus de tests à chaque étape, lorsque cela est possible.
joro
18

C'est l'un des principaux problèmes que j'ai rencontrés lorsque j'ai travaillé sur Junit et j'ai trouvé la solution suivante qui me convient:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import org.junit.runners.BlockJUnit4ClassRunner;
import org.junit.runners.model.FrameworkMethod;
import org.junit.runners.model.InitializationError;

public class OrderedRunner extends BlockJUnit4ClassRunner {

    public OrderedRunner(Class<?> clazz) throws InitializationError {
        super(clazz);
    }

    @Override
    protected List<FrameworkMethod> computeTestMethods() {
        List<FrameworkMethod> list = super.computeTestMethods();
        List<FrameworkMethod> copy = new ArrayList<FrameworkMethod>(list);
        Collections.sort(copy, new Comparator<FrameworkMethod>() {

            @Override
            public int compare(FrameworkMethod f1, FrameworkMethod f2) {
                Order o1 = f1.getAnnotation(Order.class);
                Order o2 = f2.getAnnotation(Order.class);

                if (o1 == null || o2 == null) {
                    return -1;
                }

                return o1.order() - o2.order();
            }
        });
        return copy;
    }
}

créez également une interface comme ci-dessous:

 @Retention(RetentionPolicy.RUNTIME)


@Target({ ElementType.METHOD})

public @interface Order {
public int order();
}

Supposons maintenant que vous ayez la classe A où vous avez écrit plusieurs cas de test comme ci-dessous:

(@runWith=OrderRunner.class)
Class A{
@Test
@Order(order = 1)

void method(){

//do something

}

}

L'exécution commencera donc à partir de la méthode nommée "method ()". Merci!

Aman Goel
la source
Utilisation d'un autre exécuteur JUnit avec PowerMock Depuis la version 1.6.0, PowerMock prend en charge la délégation de l'exécution du test à un autre exécuteur JUnit sans utiliser de règle JUnit. Cela laisse l'exécution du test à un autre coureur de votre choix. @RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(OrderedRunner.class)
Kyriakos Georgiopoulos
11

JUnit permet actuellement d'exécuter l'ordre des méthodes de test à l'aide d'annotations de classe:

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@FixMethodOrder(MethodSorters.JVM)
@FixMethodOrder(MethodSorters.DEFAULT)

Par défaut, les méthodes de test sont exécutées par ordre alphabétique. Donc, pour définir l'ordre des méthodes spécifiques, vous pouvez les nommer comme:

a_TestWorkUnit_WithCertainState_ShouldDoSomething b_TestWorkUnit_WithCertainState_ShouldDoSomething c_TestWorkUnit_WithCertainState_ShouldDoSomething

Vous pouvez trouver des exemples ici .

Zon
la source
6

Regardez un rapport JUnit. JUnit est déjà organisé par package. Chaque package a (ou peut avoir) des classes TestSuite, chacune exécutant à son tour plusieurs TestCases. Chaque TestCase peut avoir plusieurs méthodes de test du formulairepublic void test*() , chacune devenant en fait une instance de la classe TestCase à laquelle elles appartiennent. Chaque méthode de test (instance TestCase) a un nom et un critère de réussite / échec.

Ce dont ma direction a besoin, c'est du concept de TestStep individuel éléments , chacun indiquant ses propres critères de réussite / échec. L'échec d'une étape de test ne doit pas empêcher l'exécution des étapes de test suivantes.

Dans le passé, les développeurs de tests à ma place organisaient les classes TestCase en packages correspondant aux parties du produit testé, créaient une classe TestCase pour chaque test et faisaient de chaque méthode de test une "étape" distincte du test, complet avec ses propres critères de réussite / échec dans la sortie JUnit. Chaque TestCase est un "test" autonome, mais les méthodes individuelles, ou "étapes" de test dans le TestCase, doivent se produire dans un ordre spécifique.

Les méthodes TestCase étaient les étapes de TestCase, et les concepteurs de tests ont obtenu un critère de réussite / échec distinct par étape de test. Maintenant, les étapes du test sont mélangées et les tests échouent (bien sûr).

Par exemple:

Class testStateChanges extends TestCase

public void testCreateObjectPlacesTheObjectInStateA()
public void testTransitionToStateBAndValidateStateB()
public void testTransitionToStateCAndValidateStateC()
public void testTryToDeleteObjectinStateCAndValidateObjectStillExists()
public void testTransitionToStateAAndValidateStateA()
public void testDeleteObjectInStateAAndObjectDoesNotExist()
public void cleanupIfAnythingWentWrong()

Chaque méthode de test affirme et signale ses propres critères de réussite / d'échec. Réduire cela en "une seule grande méthode de test" pour les besoins de la commande perd la granularité des critères de réussite / échec de chaque "étape" dans le rapport de synthèse JUnit. ... et cela dérange mes managers. Ils réclament actuellement une autre alternative.

Quelqu'un peut-il expliquer comment une unité JUnit avec un ordre de méthode de test brouillé prendrait en charge des critères de réussite / échec distincts pour chaque étape de test séquentielle, comme illustré ci-dessus et requis par ma direction?

Quelle que soit la documentation, je vois cela comme une sérieuse régression dans le framework JUnit qui rend la vie difficile à de nombreux développeurs de tests.

Développeur anonyme
la source
6

Mise à jour JUnit 5 (et mon avis)

Je pense que c'est une fonctionnalité assez importante pour JUnit, si l'auteur de JUnit ne veut pas la fonctionnalité de commande, pourquoi?

Par défaut, les bibliothèques de tests unitaires n'essaient pas d'exécuter les tests dans l'ordre qui se produit dans le fichier source.
JUnit 5 comme JUnit 4 fonctionnent de cette façon. Pourquoi ? Parce que si l'ordre est important, cela signifie que certains tests sont couplés entre eux et ce n'est pas souhaitable pour les tests unitaires .
Alors le@Nested fonctionnalité introduite par JUnit 5 suit donc la même approche par défaut.

Mais pour les tests d'intégration, l'ordre de la méthode de test peut être important car une méthode de test peut changer l'état de l'application d'une manière attendue par une autre méthode de test. Par exemple, lorsque vous écrivez un test d'intégration pour un traitement de paiement dans une boutique en ligne, la première méthode de test à exécuter consiste à enregistrer un client, la seconde à ajouter des articles dans le panier et la dernière à effectuer le paiement. Si le testeur ne respecte pas cet ordre, le scénario de test est défectueux et échouera.
Ainsi, dans JUnit 5 (à partir de la version 5.4), vous avez tout de même la possibilité de définir l'ordre d'exécution en annotant la classe de test avec @TestMethodOrder(OrderAnnotation.class)et en spécifiant l'ordre avec @Order(numericOrderValue)pour les méthodes dont l'ordre est important.

Par exemple :

@TestMethodOrder(OrderAnnotation.class) 
class FooTest {

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

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

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

Production :

createUserAndLogin

addItemsInBasket

checkoutOrder

Soit dit en passant, en précisant @TestMethodOrder(OrderAnnotation.class) semble pas nécessaire (au moins dans la version 5.4.0 que j'ai testée).

Note complémentaire
À propos de la question: JUnit 5 est-il le meilleur choix pour écrire des tests d'intégration? Je ne pense pas qu'il devrait être le premier outil à considérer (Cucumber and co peut souvent apporter une valeur et des fonctionnalités plus spécifiques pour les tests d'intégration) mais dans certains cas de test d'intégration, le cadre JUnit est suffisant. C'est donc une bonne nouvelle que la fonctionnalité existe.

davidxxx
la source
4

Je ne suis pas sûr d'être d'accord, si je veux tester «Téléchargement de fichier» puis «Données insérées par téléchargement de fichier», pourquoi ne voudrais-je pas que ces données soient indépendantes les unes des autres? Parfaitement raisonnable, je pense pouvoir les exécuter séparément plutôt que d'avoir les deux dans un cas de test de Goliath.

Mattk
la source
3

Ce que vous voulez est parfaitement raisonnable lorsque les cas de test sont exécutés en tant que suite.

Malheureusement, pas le temps de donner une solution complète pour le moment, mais jetez un œil à la classe:

org.junit.runners.Suite

Ce qui vous permet d'appeler des cas de test (de n'importe quelle classe de test) dans un ordre spécifique.

Ceux-ci peuvent être utilisés pour créer des tests fonctionnels, d'intégration ou de système.

Cela laisse vos tests unitaires tels qu'ils sont sans ordre spécifique (comme recommandé), que vous les exécutiez comme ça ou non, puis réutilisez les tests dans le cadre d'une image plus grande.

Nous réutilisons / héritons le même code pour les tests unitaires, d'intégration et de système, parfois axés sur les données, parfois axés sur les validations et parfois exécutés en tant que suite.

CharlieS
la source
2
Vous n'avez pas eu le temps de compléter cette réponse depuis 2014? ;)
Charlie
2

Voir ma solution ici: "Junit et java 7."

Dans cet article, je décris comment exécuter les tests junit dans l'ordre - "tout comme dans votre code source". Les tests seront exécutés, dans l'ordre où vos méthodes de test apparaissent dans le fichier de classe.

http://intellijava.blogspot.com/2012/05/junit-and-java-7.html

Mais comme l'a dit Pascal Thivent, ce n'est pas une bonne pratique.

Kornero
la source
J'avais vu votre article de blog (en russe!), Mais c'est beaucoup trop compliqué.
Nicolas Barbulesco
1
@NicolasBarbulesco J'ai deux blogs (rus et eng). C'est trop compliqué, car vous ne devez pas créer de tests avec la dépendance de l'ordre d'exécution. Ma solution est une solution de contournement, mais la vraie solution - consiste à supprimer cette dépendance.
kornero
1
Ce message ne contient pas de réponse réelle. Veuillez envisager d'ajouter au moins l'explication de base, en plus du lien.
locale par défaut
0

J'ai lu quelques réponses et je reconnais que ce n'est pas la meilleure pratique, mais la façon la plus simple de commander vos tests - et la façon dont JUnit exécute les tests par défaut est par nom alphabétique croissant.

Nommez donc simplement vos tests dans l'ordre alphabétique que vous souhaitez. Notez également que le nom du test doit commencer par le mot test. Attention aux chiffres

test12 s'exécutera avant test2

donc:

testA_MyFirstTest testC_ThirdTest testB_ATestThatRunsSecond

pstorli
la source
0

Veuillez vérifier celui-ci: https://github.com/TransparentMarket/junit . Il exécute le test dans l'ordre où ils sont spécifiés (définis dans le fichier de classe compilé). Il comporte également une suite AllTests pour exécuter les tests définis par le sous-package en premier. En utilisant l'implémentation AllTests, on peut étendre la solution en filtrant également les propriétés (nous utilisions des annotations @Fast mais elles n'étaient pas encore publiées).

Martin Kersten
la source
0

Voici une extension de JUnit qui peut produire le comportement souhaité: https://github.com/aafuks/aaf-junit

Je sais que cela va à l'encontre des auteurs de la philosophie JUnit, mais lorsque vous utilisez JUnit dans des environnements qui ne sont pas des tests unitaires stricts (comme pratiqué en Java), cela peut être très utile.

shlomi33
la source
0

Je me suis retrouvé ici en pensant que mes tests n'étaient pas exécutés dans l'ordre, mais la vérité est que le désordre était dans mes travaux asynchrones. Lorsque vous travaillez avec la concurrence, vous devez également effectuer des vérifications de concurrence entre vos tests. Dans mon cas, les travaux et les tests partagent un sémaphore, donc les tests suivants se bloquent jusqu'à ce que le travail en cours libère le verrou.

Je sais que ce n'est pas entièrement lié à cette question, mais cela pourrait peut-être aider à cibler le problème correct

McCoy
la source
0

vous pouvez utiliser l'un de ces codes:

@FixMethodOrder(MethodSorters.JVM)OR `@FixMethodOrder(MethodSorters.DEFAULT)` OR `@FixMethodOrder(MethodSorters.NAME_ASCENDING)` before your test class like this:


@FixMethodOrder(MethodSorters.NAME_ASCENDING)


public class BookTest { ...}
Arezoo Bagherzadi
la source