Accès rapide :
Création d'un projet Eclipse avec le support JUnit 3.x
Les conventions de codage d'une classe de test JUnit 3
Lancer vos tests JUnit
Tester un déclenchement d'exceptions
Les méthodes setUp et tearDown
Bonnes pratiques de développement JUnit
Les différentes versions 3.x de JUnit ont été écrites à une époque reculée de Java. A cette époque la notion d'annotation n'était pas encore présente dans le langage. Du coup JUnit 3.x utilise des conventions de nommage pour les méthodes de tests que vous allez développer. Si vous respectez les conventions que je vais vous présenter tout se passera bien. Dans le cas contraire, tant pis pour vous ! ;-)
Dans la suite de ce document, nous allons chercher à tester le code développé durant le chapitre sur l'introduction à la programmation orientée objet. Ce code proposait une simple classe de manipulation de nombre rationnels (de fractions, si vous préférez).
Nous allons tester ce code en exploitant l'intégration de JUnit au sein d'Eclipse. Donc commencez par créer un nouveau projet : vous pouvez l'appeler
Rational
. Copiez/collez-y le code de la classe Rational
à partir du lien ci-dessus.
Il faut comprendre que mélanger les codes applicatifs et les codes de tests, n'est pas forcément une bonne chose. Eclipse propose une fonctionnalité
qui va nous aider à éviter cela : la notion de « Source Folder ». Un tel dossier contient, comme son nom l'indique, des codes sources et force
leur compilation. Clairement, le répertoire src
est un dossier de cette nature. Pour créer un dossier de codes sources dans votre projet,
cliquez avec le bouton droit de la souris sur le projet puis sélectionnez « New / Source Folder ». Appelez ce dossier test
Voici à quoi doit ressembler votre projet à ce stade.
Il faut maintenant ajouter une classe de test. Pour ce faire, cliquez avec le bouton droit de la souris sur le dossier test
et
sélectionnez-y l'assistant « New / JUnit Test Case », comme le montre la capture d'écran suivante.
Une nouvelle boîte de dialogue doit apparaître. Notez bien qu'en haut de la fenêtre, vous pouvez choisir la version de JUnit à utiliser.
Veuillez cocher la case « New JUnit 3 test ». Veuillez ensuite remplir le champ « Package » avec la valeur fr.koor.poo
ainsi que le champ « Name » avec la valeur RationalTest
.
Voici une capture d'écran de cette boîte de dialogue.
Comme c'est la première fois qu'on ajoute une classe de test à ce projet, la librairie JUnit n'y est pas encore présente. Eclipse détecte cette situation et vous propose d'ajouter automatiquement la librairie au ClassPath. Accepter, bien entendu, cette proposition.
Arrivé à ce stade, voici à quoi doit ressembler votre projet.
La première règle est assez simple : toute classe de test doit hériter directement ou indirectement de la classe junit.framework.TestCase
.
Vous ne pouvez donc pas hériter d'une autre classe qui ne soit pas compatible avec TestCase
!
Ensuite, vous allez rajouter autant de méthodes que de tests à effectuer. Il faut impérativement que vos méthodes de test aient un nom qui commence
par les quatre lettres test
. De plus, il faut impérativement que ces méthodes soient publiques, qu'elles n'aient pas de valeur de retour
(void
) et qu'elles n'acceptent aucun paramètre. Sans quoi, les méthodes ne respectant pas ces règles ne seront pas prises en compte par JUnit.
Voici un premier exemple de test qui va vérifier si l'addition de deux nombres rationnels se passe bien.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
package fr.koor.poo; import junit.framework.TestCase; public class RationalTest extends TestCase { // La méthode de test commence bien par le préfixe test. public void testAddition() { // On prépare le scénario de test Rational r1 = new Rational( 1, 3); Rational r2 = new Rational( 2, 1); Rational result = r1.add( r2 ); // On vérifie les résultats assertEquals( 7 /* Valeur attendue */, result.getNumerator() /* Valeur constatée */); assertEquals( 3 /* Valeur attendue */, result.getDenominator() /* Valeur constatée */); } } |
Les méthodes commençant par assert
sont héritées de la classe TestCase
. Elles permettent de vérifier vos résultats.
Si l'assertion est vraie, le test se poursuit normalement est aucun message d'erreur sera produit. Dans le cas contraire, ces méthodes
déclenchent des exceptions permettant d'interrompre le test et de le passer dans l'état échoué.
Vous pouvez y ajouter un second test pour vérifier si la simplification de fraction se passe correctement. Voici un exemple de code pour ce second test.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
package fr.koor.poo; import junit.framework.TestCase; public class RationalTest extends TestCase { // La méthode de test commence bien par le préfixe test. public void testAddition() { // On prépare le scénario de test Rational r1 = new Rational( 1, 3); Rational r2 = new Rational( 2, 1); Rational result = r1.add( r2 ); // On vérifie les résultats assertEquals( 7 /* Valeur attendue */, result.getNumerator() /* Valeur constatée */); assertEquals( 3 /* Valeur attendue */, result.getDenominator() /* Valeur constatée */); } // La seconde méthode de test public void testSimplify() { // On prépare le scénario de test Rational r = new Rational( 5*7*11*13, 11*13*17 ); r.simplify(); // On vérifie les résultats assertEquals( 35, r.getNumerator() ); assertEquals( 17, r.getDenominator() ); } } |
Pour lancer le jeu de tests, veuillez cliquer avec le bouton droit de la souris sur le nom de la classe et lancez l'assistant « Run As / JUnit Test ». Les résultats des tests sont affichés dans la vue « JUnit » comme le montre la capture d'écran suivante.
La vue « JUnit » affiche donc les résultats. Pour chaque test lancé, vous avez l'information sur le temps qu'il a pris durant son exécution.
On y voit aussi que nous avons démarré deux tests et que nous avons tous ces tests en succès et aucun en échec.
En conséquence, on y voit la barre de statut complétement verte. Dans le cas contraire elle aurait été rouge.
Voici un exemple de détection d'un test en échec (j'ai modifié le code de la méthode add
pour que le calcul se passe mal).
En fait, les possibilités de démarrage de vos tests sont plus subtiles qu'il n'y parait. Vous pouvez choisir « la quantité » de tests à exécuter en cliquant à différents endroits de l'interface graphique d'Eclipse.
Notez aussi que, si vous avez déjà lancé un ensemble de tests et que vous souhaitez relancer cet ensemble, vous pouvez cliquer sur le bouton « Rerun Test », comme le montre la capture d'écran ci-dessous.
Tester une application ne veut pas dire tester que ce qui doit marcher. Il faut aussi vérifier que tous les cas d'erreur connus sont bien détectés. Dans notre cas, il n'est normalement pas possible de créer une fraction avec la valeur 0 en dénominateur. Il faut donc tester qu'on détecte bien ce type de problème.
Le souci est que si une exception remonte à JUnit, le scénario de test sera considéré comme étant échoué. Comment inverser les choses ?
En fait, c'est assez simple : un banal bloc try / catch
couplé à un appel à la méthode fail
permet de gérer la difficulté.
Voici un exemple de code pour vérifier ce point.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
package fr.koor.poo; import junit.framework.TestCase; public class RationalTest extends TestCase { // Les autres méthodes de tests // ... // On teste un déclenchement d'exception. public void testBadDenominator() { try { new Rational( 1, 0 ); // Si on arrive ici, ce n'est pas normal => on plante le scénario fail( "Exception is needed" ); } catch( RuntimeException exception ) { // Yes ! On a capturé l'exception, le test est donc Ok. // On a donc rien à faire de plus. } } } |
Et voici les résultats produits par notre nouveau jeu de tests.
Pour clore ce chapitre, je voudrais porter mon attention sur deux méthodes complémentaires : les méthodes setUp
et tearDown
.
La méthode setUp
, si elle est définie, est invoquée avant l'exécution de chaque méthode de test. Dans notre classe de test, nous avons
trois méthodes préfixées par test
, la méthode setUp
sera donc lancée trois fois.
Par exemple, si chacun de vos tests nécessite une connexion à une base de données, il ne faut pas que l'exécution du test précédent mette en péril
l'exécution de la méthode de test courante, par exemple avec des transactions en cours ou encore une fermeture de connexion à la base.
Le mieux est donc de réouvrir une connexion propre avant chaque exécution de test.
De manière symétrique, il existe une méthode permettant de nettoyer un éventuel contexte : la méthode tearDown
.
Elle sera exécutée à la fin de chaque méthode de test et dans notre cas, trois fois.
Si l'on reprend l'exemple de l'utilisation d'une connexion à une base de données pour chacun de vos tests, dans ce cas, vous pouvez fermer chaque connexion
à la fin du test via la méthode tearDown
.
Outre le respect des noms de ces deux méthodes, elles doivent aussi être publiques, ne rien renvoyer et ne pas accépter de paramètre. Si vous ne respectez pas ces règles, elles seront purement et simplement ignorées.
Lors de la création d'une nouvelle classe de test, vous pouvez demander à Eclipse de produire ces méthodes en cochant les cases associées, comme le montre la capture d'écran suivante.
Voici un exemple basique de définition de ces deux méthodes : l'objectif est simplement de vous montrer qu'elles déclenchent avant est après chaque appel à une méthode de test.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
package fr.koor.poo; import junit.framework.TestCase; public class SetUpTearDownTest extends TestCase { protected void setUp() throws Exception { System.out.println( "Avant chaque méthode de test" ); } public void testMethod1() { System.out.println( "Test 1" ); } public void testMethod2() { System.out.println( "Test 2" ); } public void testMethod3() { System.out.println( "Test 3" ); } protected void tearDown() throws Exception { System.out.println( "Après chaque méthode de test"); } } |
Et voici les résultats qui seront affichés dans la console Eclipse suite à l'exécution de vos tests.
Améliorations / Corrections
Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.
Emplacement :
Description des améliorations :