2010-04-14 4 views
124

J'ai trouvé l'utilisation correcte (ou au moins la documentation) de JUnit très confuse. Cette question sert à la fois de référence future et de véritable question.JUnit confusion: utilisez 'extends TestCase' ou '@Test'?

Si je comprends bien, il existe deux approches principales pour créer et exécuter un test JUnit:

Approche A (JUnit 3 style): créer une classe qui étend TestCase, et commencer les méthodes d'essai avec le mot test. Lors de l'exécution de la classe en tant que test JUnit (dans Eclipse), toutes les méthodes commençant par le mot test sont exécutées automatiquement.

import junit.framework.TestCase; 

public class DummyTestA extends TestCase { 

    public void testSum() { 
     int a = 5; 
     int b = 10; 
     int result = a + b; 
     assertEquals(15, result); 
    } 
} 

Méthode B (JUnit 4-style): créer une classe 'normale' et préfixer une annotation @Test à la méthode. Notez que vous n'avez pas besoin de démarrer la méthode avec le mot test.

import org.junit.*; 
import static org.junit.Assert.*; 

public class DummyTestB { 

    @Test 
    public void Sum() { 
     int a = 5; 
     int b = 10; 
     int result = a + b; 
     assertEquals(15, result); 
    } 
} 

Le mélange des deux ne semble pas être une bonne idée, voir par ex. this stackoverflow question:

Maintenant, mes questions (s):

  1. Quelle est l'approche préférée, ou quand voulez-vous utiliser une place de l'autre?
  2. L'approche B permet de tester les exceptions en étendant l'annotation @Test comme dans @Test(expected = ArithmeticException.class). Mais comment testez-vous les exceptions lors de l'utilisation de l'approche A?
  3. Lorsque vous utilisez l'approche A, vous pouvez regrouper un certain nombre de classes de test dans une suite de test comme celui-ci:

    TestSuite suite = new TestSuite("All tests");
    suite.addTestSuite(DummyTestA.class);
    suite.addTestSuite(DummyTestAbis.class);

    Mais cela ne peut pas être utilisé avec l'approche B (puisque chaque testclass devrait sous-classer TestCase). Quelle est la bonne façon de grouper les tests pour l'approche B?

Edit: J'ai ajouté les versions de JUnit à la fois des approches

Répondre

96

La distinction est assez facile:

  • extension TestCase est le la façon dont les tests unitaires ont été écrits dans JUnit 3 (bien sûr, il est toujours supporté dans JUnit 4)
  • en utilisant l'@Test l'annotation est la manière introduite par JUnit 4

En général, vous devez choisir le chemin d'annotation, à moins qu'une compatibilité avec JUnit 3 (et/ou une version Java antérieure à Java 5) ne soit nécessaire. La nouvelle stratégie présente plusieurs avantages:

Pour tester des exceptions prévues dans un JUnit 3 TestCase vous auriez à rendre le texte explicite.

public void testMyException() { 
    try { 
    objectUnderTest.myMethod(EVIL_ARGUMENT); 
    fail("myMethod did not throw an Exception!"); 
    } catch (MyException e) { 
    // ok! 
    // check for properties of exception here, if desired 
    } 
} 
+0

Réponse utile et approfondie, mais je ne comprends pas complètement "vérifier le message d'exception". Vérification contre une chaîne codée en dur va être un cauchemar de maintenance. Vous devez avoir voulu dire "vérifiez les propriétés de votre type d'exception spécifique". – thSoft

+3

@thSoft: ce n'est pas souvent que c'est utilisé, mais parfois je veux m'assurer que la méthode d'exception mentionne le champ offensant, par exemple. Puis un simple 'assertTrue (e.getMessage(). Contains (" foo "))' pourrait être utile. –

+1

Même dans JUnit4, il s'agit d'un idiome important lorsque vous devez vérifier le message ou une autre propriété de l'exception (telle que la cause). La méthode 'expected' vérifie uniquement le type. – Yishai

21

J'ai une préférence pour JUnit 4 (approche Annotation) parce que je trouve plus flexible.

Si vous voulez construire suite de tests dans JUnit 4, vous devez créer une classe regroupant tous les tests comme celui-ci:

import org.junit.runner.RunWith; 
import org.junit.runners.Suite; 
import org.junit.runners.Suite.SuiteClasses; 


@RunWith(Suite.class) 
@SuiteClasses({ 
    Test1.class, 
    Test2.class, 
    Test3.class, 
    Test4.class 
})public class TestSuite 
{ 
/* empty class */ 
} 
1
  1. L'approche « préférée » serait d'utiliser des annotations qui ont été introduites depuis Junit 4. Ils font beaucoup de choses plus faciles (voir votre deuxième question)

  2. Vous pouvez utiliser un simple, bloc try/catch pour que:


public void testForException() { 
    try { 
     Integer.parseInt("just a string"); 
     fail("Exception should have been thrown"); 
    } catch (final Exception e) { 
     // expected 
    } 
} 
3

Vous devez utiliser JUnit 4. Il est préférable.

De nombreux frameworks ont commencé à désapprouver le support de JUnit 3.8.

C'est du ressort 3.0 documentation de référence:

[Avertissement] Héritage JUnit 3.8 classe hiérarchie est dépréciée

En général, vous devez toujours essayer d'utiliser la dernière version stable de un cadre lorsque vous démarrez quelque chose de nouveau.

14

Il y a une partie sans réponse à votre question, et c'est "Quelle est la bonne façon de grouper les tests pour l'approche B?"

La réponse officielle est que vous annotez une classe avec @RunWith (Suite.class), puis utilisez l'annotation @ Suite.SuiteClasses pour répertorier les classes. C'est ainsi que les développeurs de JUnit le font (listant chaque classe dans une suite manuellement). À bien des égards, cette approche est une amélioration, car elle est triviale et intuitive à ajouter avant les comportements suite et suite (il suffit d'ajouter une méthode @BeforeClass et @AfterClass à la classe annotée avec le @RunWith - beaucoup mieux que l'ancien TestFixture).

Cependant, il ya un retour en arrière, car les annotations ne vous permettent pas de créer dynamiquement la liste des classes, et contourner ce problème devient un peu moche. Vous devez sous-classer la classe Suite et créer dynamiquement le tableau de classes dans la sous-classe et le transmettre au constructeur Suite, mais ceci est une solution incomplète dans le sens où les autres sous-classes de Suite ne fonctionnent pas et ne supporte pas la collection de classes de tests dynamiques.

+1

+1 pour cela. Après m'être lancé dans une tâche d'écriture d'une solution dynamique pour ajouter des tests à une suite TestSuite, j'ai dû étendre TestCase à chacun de mes tests. Ceci à son tour a brisé les tests unitaires qui utilisaient auparavant les annotations JUnit4 pour définir les exceptions attendues. Ma recherche d'un moyen de remplir dynamiquement une suite de tests m'a conduit à ce fil, et plus précisément à votre réponse, qui, je crois, est l'une des rares raisons souhaitables de continuer avec JUnit 3. – 8bitjunkie

Questions connexes