web-dev-qa-db-fra.com

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

J'ai une suite de tests d'intégration. J'ai une classe IntegrationTestBase à étendre pour tous mes tests. Cette classe de base a une méthode @Before (public void setUp()) et @After (public void tearDown()) pour établir des connexions API et DB. Ce que j'ai fait est simplement de surcharger ces deux méthodes dans chaque cas de test et d'appeler super.setUp() et super.tearDown(). Cependant, cela peut causer des problèmes si quelqu'un oublie d'appeler le super ou le met au mauvais endroit et qu'une exception est levée et qu'il oublie d'appeler le super finalement ou quelque chose du genre.

Ce que je veux faire est de créer les méthodes setUp et tearDown sur la classe de base final, puis d'ajouter nos propres annotées @Before Et @After méthodes. En faisant quelques tests initiaux, il semble appeler toujours dans cet ordre:

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

mais je suis juste un peu inquiet que l'ordre ne soit pas garanti et qu'il puisse causer des problèmes. J'ai regardé autour de moi et je n'ai rien vu sur le sujet. Est-ce que quelqu'un sait si je peux le faire 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?* }
}
126
Joel

Oui, ce comportement est garanti:

@Before :

Les méthodes @Before Des superclasses seront exécutées avant celles de la classe actuelle, sauf si elles sont remplacées dans la classe actuelle. Aucune autre commande n'est définie.

@After :

Les méthodes @After Déclarées dans les superclasses seront exécutées après celles de la classe actuelle, sauf si elles sont remplacées dans la classe actuelle.

127
axtavt

Un potentiel acquis qui m'a mordu avant:

J'aime avoir au plus une méthode @Before Dans chaque classe de test, car l'ordre d'exécution des méthodes @Before Définies dans une classe n'est pas garanti. En règle générale, j'appellerai une telle méthode setUpTest().

Toutefois, bien que @Before Soit documenté sous la forme 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 avec @Before A un nom unique dans la hiérarchie de classes.

Par exemple, j'ai eu ce qui suit:

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

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

Je m'attendais à ce que AbstractFooTest.setUpTest() s'exécute avant FooTest.setUpTest(), mais seul 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();
    ...
  }
}
48
John Q Citizen

Je pense que basé sur la documentation de la @Before et @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");
  }
}

et

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 à la suite

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 la classe AbstractBaseTest ne peuvent pas écraser les méthodes setUp/tearDown par accident. S'ils le souhaitent, ils doivent connaître le nom exact et pouvoir le faire.

Inconvénient (mineur) de cette approche: les utilisateurs ne peuvent pas voir qu'il se passe des choses avant ou après leur setUp/tearDown. Ils doivent savoir que ces éléments sont fournis par la classe abstraite. Mais je suppose que c'est la raison pour laquelle ils utilisent la classe abstraite

20
Matthias Hoefel

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

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

Ce n’est pas exactement ce que vous voulez, mais votre connexion à la base de données sera maintenue ouverte pendant toute la durée de vos tests, puis elle se fermera une fois pour toutes à la fin.

2
Swati

Ce n'est pas une réponse à la question du slogan, mais c'est une réponse aux problèmes mentionnés dans le corps de la question. Au lieu d'utiliser @Before ou @After, envisagez d'utiliser @ org.junit.Rule car cela vous donne plus de flexibilité. ExternalResource (version 4.7) est la règle qui vous intéressera le plus si vous gérez des connexions. En outre, si vous souhaitez que l'ordre d'exécution de vos règles soit garanti, utilisez un RuleChain (à partir de 4.10). Je crois que tous étaient disponibles lorsque cette question a été posée. L'exemple de code ci-dessous est copié à partir des javadocs de ExternalResource.

 public static class UsesExternalResource {
  Server myServer= new Server();

  @Rule
  public ExternalResource resource= new ExternalResource() {
      @Override
      protected void before() throws Throwable {
          myServer.connect();
         };

      @Override
      protected void after() {
          myServer.disconnect();
         };
     };

  @Test
  public void testFoo() {
      new Client().run(myServer);
     }
 }
2
successhawk

Si vous inversez les choses, vous pouvez déclarer votre résumé de classe de base et laisser les descendants déclarer les méthodes setUp et tearDown (sans annotations) appelées dans les méthodes setUp et tearDown annotées de la classe de base.

2
Buhb