web-dev-qa-db-fra.com

setUp / tearDown (@ Before / @ After) pourquoi nous en avons besoin dans JUnit?

Je crois que nous savons tous que setUp (@Before) s'exécutera avant toute méthode de test et tearDown (@After) s'exécutera après la méthode de test.

Nous savons également que Junit créera une instance de Test par méthode de test.

ma question est que pouvons-nous simplement déplacer le contenu de la méthode setUp vers le constructeur de classe et supprimer la méthode setUp? y a-t-il une raison particulière de conserver la méthode setUp?

61
mhshams

Cet (ancien) meilleures pratiques JUnit l'article le dit comme ceci:

N'utilisez pas le constructeur de cas de test pour configurer un cas de test

La configuration d'un scénario de test dans le constructeur n'est pas une bonne idée. Considérer:

public class SomeTest extends TestCase
   public SomeTest (String testName) {
      super (testName);
      // Perform test set-up
   }
}

Imaginez que lors de l'exécution de l'installation, le code d'installation lance un IllegalStateException. En réponse, JUnit lançait un AssertionFailedError, indiquant que le scénario de test ne pouvait pas être instancié. Voici un exemple de la trace de pile résultante:

junit.framework.AssertionFailedError: Cannot instantiate test case: test1   
    at junit.framework.Assert.fail(Assert.Java:143)
    at junit.framework.TestSuite.runTest(TestSuite.Java:178)
    at junit.framework.TestCase.runBare(TestCase.Java:129)
    at junit.framework.TestResult.protect(TestResult.Java:100)
    at junit.framework.TestResult.runProtected(TestResult.Java:117)
    at junit.framework.TestResult.run(TestResult.Java:103)
    at junit.framework.TestCase.run(TestCase.Java:120)
    at junit.framework.TestSuite.run(TestSuite.Java, Compiled Code)
    at junit.ui.TestRunner2.run(TestRunner.Java:429)

Cette trace de pile s'avère peu informative; cela indique seulement que le cas de test n'a pas pu être instancié. Il ne détaille pas l'emplacement ou le lieu d'origine de l'erreur d'origine. Ce manque d'information rend difficile de déduire la cause sous-jacente de l'exception.

Au lieu de configurer les données dans le constructeur, effectuez la configuration du test en remplaçant setUp(). Toute exception levée dans setUp() est signalée correctement. Comparez cette trace de pile avec l'exemple précédent:

Java.lang.IllegalStateException: Oops
    at bp.DTC.setUp(DTC.Java:34) 
    at junit.framework.TestCase.runBare(TestCase.Java:127)
    at junit.framework.TestResult.protect(TestResult.Java:100)
    at junit.framework.TestResult.runProtected(TestResult.Java:117)
    at junit.framework.TestResult.run(TestResult.Java:103)
    ...

Cette trace de pile est beaucoup plus informative; il montre quelle exception a été levée (IllegalStateException) et d'où. Cela permet d'expliquer beaucoup plus facilement l'échec de la configuration de test.

58
Pascal Thivent

Au travail, nous avons découvert quelque chose d'assez intéressant qui répond à votre question. Lorsque vous exécutez une suite de tests, en particulier un grand nombre de tests (200+), JUnit commence à utiliser BEAUCOUP de mémoire, car TOUS les tests sont instanciés avant l'exécution de toute méthode de test réelle.

Nous avons rencontré une "fuite de mémoire" à cause de cela parce que nous avons utilisé Spring pour câbler certains objets JPA EntiryManager pour nos tests de base de données, cela est devenu BEAUCOUP d'objets et beaucoup de mémoire et à mi-chemin des tests que nous obtenions des exceptions OutOfMemory .

À mon humble avis, la meilleure pratique consiste à utiliser setUp et tearDown pour injecter vos dépendances et annuler toutes les références de classe, cela rendra vos tests plus rapides et vous évitera beaucoup de maux de tête!

J'espère que vous apprendrez de nos erreurs :)

24
BjornS

Voici 3 bonnes raisons. En résumé:

  1. Certaines situations peuvent préférer différer la configuration des montages de test aussi longtemps que possible, à juste avant le scénario de test s'exécute.

  2. Certains cas de test peuvent faire partie d'une hiérarchie d'héritage de cas de test approfondie. Il peut être préférable de différer la configuration des montages de test jusqu'à ce que la hiérarchie complète des constructeurs soit terminée.

  3. Vous obtenez de meilleurs diagnostics si le code d'installation échoue dans setUp () plutôt que s'il échoue dans le constructeur.

1. Différez la configuration des appareils juste avant le scénario de test

Conception pour l'utilisabilité
http://www.artima.com/weblogs/viewpost.jsp?thread=70189

... Et comme Elliotte Rusty Harold l'a dit, si vous allez créer une nouvelle instance TestCase pour chaque méthode de test, "pourquoi diable s'embêter avec une méthode setUp ()?" Vous peut simplement utiliser le constructeur TestCase.

J'ai entendu Bruce Eckel souligner qu'il y a une subtile différence entre la création de votre appareil dans setUp () et sa création dans le constructeur TestCase. JUnit crée toutes les instances TestCase à l'avance, et puis pour chaque instance, appelle setup (), la méthode de test et tearDown (). En d'autres termes, la différence subtile est que les constructeurs sont tous invoqués en batch à l'avance, tandis que la méthode setUp () est appelée juste avant chaque méthode de test. Mais cela ne semble pas être une différence de pratique aussi utile.

2. Différez la configuration des appareils jusqu'à ce que tous les cas de test soient instanciés

ETutorial's Java Extreme Programming - 4.6 Set Up and Tear Down
http://etutorials.org/Programming/Java+extreme+programming/Chapter+4.+JUnit/4.6+Set+Up+and+Tear+Down/

Vous vous demandez peut-être pourquoi vous devriez écrire une méthode setUp () au lieu de simplement initialiser des champs dans le constructeur d'un scénario de test. Après tout, puisqu'une nouvelle instance du cas de test est créée pour chacune de ses méthodes de test, le constructeur est toujours appelé avant setUp (). Dans la grande majorité des cas, vous pouvez utiliser le constructeur au lieu de setUp () sans aucun effet secondaire.

Dans les cas où votre cas de test fait partie d'une hiérarchie d'héritage plus profonde, vous souhaiterez peut-être reporter l'initialisation de l'objet jusqu'à ce que les instances des classes [test] dérivées soient entièrement construites. C'est une bonne raison technique pour laquelle vous voudrez peut-être utiliser setUp () au lieu d'un constructeur pour l'initialisation. L'utilisation de setUp () et tearDown () est également utile à des fins de documentation, simplement parce qu'elle peut rendre le code plus facile à lire.

3. De meilleurs diagnostics en cas d'échec de l'installation

Meilleures pratiques JUnit (JavaWorld)
http://www.javaworld.com/jw-12-2000/jw-1221-junit.html

La configuration d'un scénario de test dans le constructeur n'est pas une bonne idée. ...

Imaginez [dans le code où l'installation est effectuée dans le constructeur de scénario de test] que lors de l'exécution de l'installation, le code d'installation lève une exception IllegalStateException. En réponse, JUnit lançait une AssertionFailedError, indiquant que le scénario de test ne pouvait pas être instancié. ...

Cette trace de pile [d'une exception levée dans le code de configuration dans le constructeur de scénario de test] s'avère plutôt non informative; cela indique seulement que le cas de test n'a pas pu être instancié.

Au lieu de configurer les données dans le constructeur, effectuez la configuration de test en remplaçant setUp (). Toute exception levée dans setUp () est signalée correctement. ...

Cette trace de pile [d'une exception levée dans la méthode setUp () au lieu du constructeur de cas de test] est beaucoup plus informative; il montre quelle exception a été levée (IllegalStateException) et d'où. Cela facilite grandement l'explication de l'échec de la configuration de test.

23
Bert F

Un coureur personnalisé tel que SpringJUnit4ClassRunner peut avoir besoin d'exécuter certains codes entre le constructeur et @Before méthode. Dans ce cas, le coureur peut injecter une dépendance que le @Before besoins des méthodes. Mais l'injection de dépendances ne peut être exécutée qu'après la construction de l'objet.

6
RichN

La raison pour laquelle vous en avez besoin est que pour de nombreux tests, vous devez souvent initialiser l'état avant chaque test afin que les tests puissent tous faire des hypothèses sur l'état de démarrage dans lequel ils s'exécutent.

Supposons que votre classe de test se termine, par exemple l'accès à la base de données. Après chaque test, vous souhaitez supprimer toutes les modifications que vos tests ont apportées à la base de données - si vous ne le faites pas, chaque test s'exécute sur une base de données légèrement modifiée. En outre, un test donné peut voir un ensemble différent de modifications si un sous-ensemble des tests précédents a échoué. Par exemple, supposons que test1 fasse une insertion, test2 vérifie que vous lisez avec précision la taille du tableau. Jour 1, test1 échoue et 0 est correct. Jour 2, test1 réussit et 1 est correct?

BTW, junit prend également en charge @BeforeClass si vous souhaitez effectuer une configuration globale et que la configuration et le démontage sont facultatifs.

3
Steve B.