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