146 votes

Dans quel ordre les @Before/@After de Junit sont-ils appelés ?

J'ai une suite de tests d'intégration. J'ai une IntegrationTestBase pour que tous mes tests soient étendus. Cette classe de base a un @Before ( public void setUp() ) et @After ( public void tearDown() ) pour établir les connexions API et DB. Ce que j'ai fait, c'est simplement surcharger ces deux méthodes dans chaque testcase et appeler super.setUp() y super.tearDown() . Cependant, cela peut poser des problèmes si quelqu'un oublie d'appeler le super ou le place au mauvais endroit et qu'une exception est levée et qu'il oublie d'appeler le super dans le finally ou autre.

Ce que je veux faire, c'est que le setUp y tearDown sur la classe de base final et ensuite ajouter notre propre annotation @Before y @After méthodes. En effectuant quelques tests initiaux, il semble que les appels se fassent toujours dans cet ordre :

Base @Before
Test @Before
Test
Test @After
Base @After

mais je m'inquiète un peu du fait que la commande n'est pas garantie et que cela pourrait poser des problèmes. J'ai cherché et je n'ai rien vu sur le sujet. Quelqu'un sait-il si je peux faire cela et ne pas avoir de problèmes ?

Code :

public class IntegrationTestBase {

    @Before
    public final void setUp() { *always called 1st?* }

    @After
    public final void tearDown() { *always called last?* }
}

public class MyTest extends IntegrationTestBase {

    @Before
    public final void before() { *always called 2nd?* }

    @Test
    public void test() { *always called 3rd?* }

    @After
    public final void after() { *always called 4th?* }
}

1 votes

Est-ce que le MyTest il manque un extends ?

0 votes

@aioobe : plus maintenant :)

150voto

axtavt Points 126632

Oui, ce comportement est garanti :

@Before :

El @Before Les méthodes des superclasses seront exécutées avant celles de la classe courante, à moins qu'elles ne soient surchargées dans la classe courante. Aucun autre ordre n'est défini.

@After :

El @After Les méthodes déclarées dans les superclasses seront exécutées après celles de la classe courante, sauf si elles sont surchargées dans la classe courante.

20 votes

Pour être clair, l'ordre d'exécution de toutes les @Before n'est pas garantie. S'il y a 10 @Before chacune d'entre elles peut être exécutée dans n'importe quel ordre, juste avant toute autre méthode.

8 votes

Donc, au lieu de citer la documentation quelque peu ambiguë, pouvez-vous l'expliquer avec vos propres mots ? Est-ce que @Before y @After les méthodes exécutées avant chaque une autre méthode de classe (une fois par méthode), ou juste avant et après la série complète de méthodes de classe (une fois par classe) ?

8 votes

Voir la prise importante déclarée par John Q Citizen : "ceci ne s'applique que si chaque méthode marquée avec @Before a un nom unique dans la hiérarchie de la classe" Très important à retenir !

58voto

John Q Citizen Points 241

Un piège potentiel qui m'a déjà mordu :

J'aime avoir au plus un @Before dans chaque classe de test, car l'ordre d'exécution de la méthode @Before Les méthodes définies dans une classe ne sont pas garanties. Typiquement, je vais appeler une telle méthode setUpTest() .

Mais, bien que @Before est documenté comme The @Before methods of superclasses will be run before those of the current class. No other ordering is defined. cela ne s'applique que si chaque méthode marquée par @Before a un nom unique dans la hiérarchie des classes.

Par exemple, j'avais les éléments suivants :

public class AbstractFooTest {
  @Before
  public void setUpTest() { 
     ... 
  }
}

public void FooTest extends AbstractFooTest {
  @Before
  public void setUpTest() { 
    ...
  }
}

Je m'attendais AbstractFooTest.setUpTest() à exécuter avant FooTest.setUpTest() mais seulement FooTest.setupTest() a été exécuté. AbstractFooTest.setUpTest() n'a pas été appelé du tout.

Le code doit être modifié comme suit pour fonctionner :

public void FooTest extends AbstractFooTest {
  @Before
  public void setUpTest() {
    super.setUpTest();
    ...
  }
}

1 votes

Pourquoi ne pas simplement changer le nom de la méthode @Before dans la classe de base ? Cela vous évitera d'avoir à appeler super dans tous les enfants... Quoi qu'il en soit, vous avez bien fait de conserver les mêmes noms.

31 votes

Juste une remarque pour rendre les choses plus sûres : Pour éviter les conflits de noms, vous pouvez faire en sorte que le @Before / @After méthode(s) dans la classe de base final Le compilateur se plaindra donc si vous essayez (accidentellement) de les surcharger dans la sous-classe.

6 votes

La méthode parentale du même nom qui n'est pas exécutée ne ressemble pas à un comportement de JUnit. Cela ressemble à la façon dont la surcharge de base fonctionne dans la POO. La méthode parent n'existe pas au moment de l'exécution. L'enfant la remplace à toutes fins utiles. C'est ainsi que fonctionne Java.

26voto

Matthias Hoefel Points 51

Je pense que d'après la documentation de la @Before y @After la bonne conclusion est de donner aux méthodes des noms uniques. J'utilise le modèle suivant dans mes tests :

public abstract class AbstractBaseTest {

  @Before
  public final void baseSetUp() { // or any other meaningful name
    System.out.println("AbstractBaseTest.setUp");
  }

  @After
  public final void baseTearDown() { // or any other meaningful name
    System.out.println("AbstractBaseTest.tearDown");
  }
}

y

public class Test extends AbstractBaseTest {

  @Before
  public void setUp() {
    System.out.println("Test.setUp");
  }

  @After
  public void tearDown() {
    System.out.println("Test.tearDown");
  }

  @Test
  public void test1() throws Exception {
    System.out.println("test1");
  }

  @Test
  public void test2() throws Exception {
    System.out.println("test2");
  }
}

donner en conséquence

AbstractBaseTest.setUp
Test.setUp
test1
Test.tearDown
AbstractBaseTest.tearDown
AbstractBaseTest.setUp
Test.setUp
test2
Test.tearDown
AbstractBaseTest.tearDown

Avantage de cette approche : Les utilisateurs de l AbstractBaseTest ne peut pas remplacer la classe setUp / tearDown méthodes par accident. S'ils le veulent, ils doivent en connaître le nom exact et peuvent le faire.

Inconvénient (mineur) de cette approche : Les utilisateurs ne peuvent pas voir qu'il y a des choses qui se passent avant ou après leur setUp / tearDown . Ils ont besoin de savoir que ces choses sont fournies par la classe abstraite. Mais je suppose que c'est la raison pour laquelle ils utilisent la classe abstraite.

2 votes

Bel exemple - il serait encore plus illustratif si vous aviez deux méthodes @Test, de sorte que l'on puisse voir que les méthodes setUp et tearDown se referment. cada méthode d'essai.

0 votes

Je pense que c'est la base de la meilleure réponse à l'OP, mais vous devriez compléter votre réponse pour être autonome. Pourriez-vous compléter votre exemple pour couvrir également les alternatives que d'autres ont suggérées, et expliquer pourquoi votre proposition est supérieure ?

2voto

Swati Points 8415

Vous pouvez utiliser @BeforeClass pour s'assurer que setup() est toujours appelé en premier. De même, vous pouvez utiliser @AfterClass pour s'assurer que tearDown() est toujours appelé en dernier.

Ce n'est généralement pas recommandé, mais c'est supporté par .

Ce n'est pas exactement ce que vous voulez, mais cela permet de garder la connexion à la base de données ouverte pendant toute la durée de vos tests, puis de la fermer une fois pour toutes à la fin.

2 votes

En fait, si vous deviez faire cela, je recommanderais de créer une méthode setupDB() y closeDB() et les marquer avec @BeforeClass y @AfterClass et remplacer vos méthodes avant/après par setup() y tearDown()

0 votes

Méthodes annotées avec @BeforeClass y @AfterClass doivent être statiques. Qu'en est-il du cas où nous voulons utiliser des variables d'instance dans ces méthodes ?

0 votes

Un avertissement lorsque vous utilisez @BeforeClass avec Powermock : cela ne fonctionne que pour le premier essai. Voir ce problème : github.com/powermock/powermock/issues/398

2voto

Buhb Points 3110

Si vous inversez les choses, vous pouvez déclarer votre classe de base abstraite, et faire en sorte que les descendants déclarent des méthodes setUp et tearDown (sans annotations) qui sont appelées dans les méthodes setUp et tearDown annotées de la classe de base.

1 votes

Ce n'est pas une mauvaise idée, mais je ne veux pas imposer un contrat aux tests qui n'ont pas besoin de leur propre setUp/tearDown

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X