JUnit

Introduction

JUnit fait parti des outils fantastiques qui change la vie de tout bon développeur. Non seulement il améliore considérablement la qualité des logiciels conçus, tout en restant simple à mettre en œuvre et à apprendre! Que demander de plus?

Le principe est simple, afin de vérifier le code produit, on va écrire un test qui va se charger de valider le bon fonctionnement de ce code, en s’assurant que le résultat obtenu est bien celui qu’on attendait à l’exécution du code. Vous allez voir dans ce tutoriel comment mettre en œuvre un test unitaire pour valider le code contenu dans une fonction. C’est un jeu d’enfant!

Site Officiel

Vous trouverez le site officiel de JUnit à l’adresse suivante : http://www.junit.org/

Wikipédia

Je vous invite à consulter les articles français et anglais de la célèbre encyclopédie Wikipédia à propos de JUnit.

Tutoriel

Avant d’entamer ce tutoriel, vérifiez que vous ayez bien les pré requis :

  • Le JDK doit être installé sur votre machine.
  • Eclipse doit être installé sur votre machine (il est conseillé d’utiliser la version Eclipse IDE for Java EE Developers pour ce tutoriel).


Création du projet dans Eclipse

  • Lancer Eclipse.
  • On crée un nouveau projet. Pour cela on ouvre le menu File → New → Java Project.
  • La fenêtre de wizard apparaît alors. Nous allons simplement indiquer ici le nom du projet dans le champ Project name : sf-tutorial-junit
  • On peut ensuite cliquer sur Finish. Le projet apparait alors dans l’onglet Project Explorer qui répertorie les différents projets du workspace.


Ajout des librairies

  • Il faut télécharger la librairie JUnit à l’adresse : http://downloads.sourceforge.net/junit/junit-4.5.jar?use_mirror=
  • On va ensuite copier l’archive jar dans notre projet. Pour ce faire, on ouvre un explorateur de fichiers, pour accéder au workspace (sous Ubuntu il sera situé par défaut dans /home/user/workspace), on va dans le répertoire du projet, dans lequel on créé un nouveau répertoire lib qui contiendra les librairies. On colle ensuite le fichier jar dedans.

Si le répertoire lib n’apparait pas dans votre projet sous Eclipse, sélectionnez votre projet et appuyez sur F5 pour rafraîchir le projet.

  • Il faut ensuite spécifier à Eclipse dans notre projet qu’on a ajouté une librairie pour pouvoir l’utiliser. On fait un clic droit sur le projet dans le package explorer qui ouvre un menu contextuel : on ouvre Build Path → Configure Build Path. On clic sur l’onglet Libraries, puis sur le bouton add JARS…. Il suffit ensuite de sélectionner le fichier junit-4.5.jar situé dans le répertoire lib du projet sf-tutorial-junit. On valide les modifications sur le projet en cliquant sur OK. Nous sommes maintenant prêts à débuter le code!


Création de la classe

Nous allons créer une classe java tout ce qu’il y a de plus classique. C’est un exemple volontairement simpliste afin de se concentrer sur JUnit.

  • On se place sur le répertoire src du projet sf-tutorial-junit, puis on fait un clic droit, New → Class.
  • La fenêtre de wizard New Java Class est normalement apparue. On indique dans le champ Package com.scub.foundation.tutorial.junit et on entre dans Name le nom Calculatrice puis on clique sur Finish.
  • La classe a du être créé, on va coder maintenant la fonction « additionner » telle qu’elle est indiquée ci-dessous :
package com.scub.foundation.samples.junit;
 
/**
 * Modélise les fonctionnalités d'une calculatrice.
 * @author Scub-Foundation
 */
public class Calculatrice {
 
	/**
	 * Additionne deux entiers.
	 * @param a premier entier
	 * @param b deuxième entier
	 * @return la somme des deux entiers
	 */
	public int additionner(int a, int b) {
		return a + b;
	}
 
}


Création du test unitaire

Maintenant que la classe est écrite, nous allons écrire un test qui valide son comportement et nous assurer qu’elle répond bien aux spécifications.

  • Placez vous sur le dossier src du projet sf-tutorial-junit, clic droit dessus New → Other →JUnit Test Case
  • La fenêtre de wizard New JUnit Test Case est normalement apparue. On indique dans le champ Package com.scub.foundation.tutorial.junit et on entre dans Name le nom CalculatriceTest puis on coche la case setUp() et on clique enfin sur Finish.
  • La classe a du être créé dans le répertoire src. On y ajoute le code suivant :
package com.scub.foundation.samples.junit;
 
import static junit.framework.Assert.assertEquals;
import org.junit.Before;
import org.junit.Test;
 
/**
 * Classe de test unitaire de Calculatrice.
 * @author Scub-Foundation
 *
 */
public class CalculatriceTest {
 
	private Calculatrice maCalculatrice;
 
	/**
	 * Méthode appelée à l'initialisation de la classe de test Unitaire.
	 */
	@Before
	public void setUp(){
		maCalculatrice = new Calculatrice();
	}
 
	/**
	 * Test de la fonction additionner().
	 */
	@Test
	public void testAdditionner(){
		// Déclaration des variables.
		final int premierEntier = 2;
		final int deuxiemeEntier = 3;
 
		// On utilise la fonction et on récupère le résultat produit.
		int resultat = maCalculatrice.additionner(premierEntier, deuxiemeEntier);
 
		// On utilise JUnit pour vérifier que le résultat de l'addition
		// est bien celui que l'on attend
		assertEquals("Résultat incorrect", premierEntier + deuxiemeEntier, resultat);
 
	}
}

On retiendra trois points importants dans cet exemple de classe :

  • On a ajouté un attribut nommé « maCalculatrice » qui correspond à l’instance que l’on va tester.
  • La fonction « setUp() » est la fonction d’initialisation de la classe. Ici, elle va instancier la classe à tester.
  • Enfin, la fonction « testAdditionner() » vérifie que la fonction « additionner » de la classe Calculatrice fonctionne correctement.

Les deux premières lignes déclarent deux variables de type entier, qu’on additionne avec la fonction « additionner() » de la classe Calculatrice. On compare ensuite le résultat de la fonction additionner() avec l’opérateur + de Java, pour être sûr que la fonction marche bien. Pour cela on utilise la fonction » assertEquals() » de JUnit, qui prend trois paramètres :

  • Le texte qui sera affiché si les deux autres paramètres ne sont pas égaux.
  • La valeur qu’on s’attend à trouver.
  • La valeur à tester.
      Avec ce simple test, si la variable résultat est égale à 5, le test passe. Si jamais un développeur modifie la fonction additionner, et change le + en – par exemple, alors le test unitaire ne sera plus valide, et on pourra alors corriger la fonction.
      JUnit dispose de quelques fonctions très pratiques (et suffisantes) pour effectuer ces tests :

      • assertEquals
      • assertTrue / assertFalse
      • assertNull / assertNotNull
      • assertSame / assertNotSame
      • fail

Les @Test et @Before présents au dessus des fonctions dans la classe CalculatriceTest sont des annotations Java, c’est une façon d’ajouter des méta-données au code Java, accessibles au programmeur à l’exécution. Cette technique est une alternative au XML. Ces annotations peuvent être ajoutées aux classes, méthodes, attributs, paramètres, variables locales et paquets.

Lancement du test unitaire

      • Nous allons exécuter la classe de test CalculatriceTest. Dans l’explorateur de package, on fait un clic droit sur cette classe Run As > JUnit Test.
      • Un onglet JUnit va alors apparaître, qui va lister les tests lancés, et le résultat de leur exécution. La copie d’écran ci dessous montre le résultat obtenu.



Gestion du lancement du test unitaire avec Eclemma

Pour lancer le test unitaire avec Eclemma il faut d’abord installer le plugin Eclemma.
Une fois le plugin installé, vous pouvez lancer EclEmma de la manière suivante:
Placez-vous sur la classe qui contient le test, faites un clic droit sur la classe puis allez sur Coverage As → JUnit Test.
Une fois le test exécuté, vous pouvez remarquer dans le code ce qui suit:

      • Les parties du code couvertes par le test sont colorées en vert.
      • Les parties du code non couvertes par le test sont colorées en rouge.

Dans la perspective Coverage, sous l’onglet Coverage le pourcentage de couverture du test est affiché.

Pourquoi utiliser JUnit?

Bien entendu, l’exemple présenté ici est trivial, mais sur de vrais projets plus complexes, il y a des centaines de méthodes à tester pour valider l’ensemble des fonctionnalités de l’application.

Produire du code de meilleur qualité

Le premier avantage, le plus évident, est bien évidemment qu’avec des tests unitaires, on peut valider une application entièrement mais aussi au fur et à mesure. Cela permet une meilleure évaluation lors des problèmes rencontrés. Prenons l’exemple d’une application de gestion commerciale, on aura un test unitaire pour valider la gestion des remises sur les lignes de devis.

Ce test sera une simple fonction sur une ligne, et va ensuite re vérifier que le montant du devis a bien pris en compte la remise. Grâce à ce test, peu importe les évolutions, les travaux réalisés, les modifications… on vérifiera systématiquement que la gestion des remises fonctionne.

Mieux gérer les retours de bugs

Il n’y a rien de plus agaçant pour le client que de voir un bug revenir, et cela arrive en l’absence de tests unitaires, car rien ne garantit avant une livraison qu’on a re testé l’ensemble des bugs déjà soumis. Avec les tests unitaires, voici la procédure à suivre :

      • Lorsqu’un client rapporte un bug, on lui assigne un numéro.
      • On crée un test unitaire correspondant, par exemple : testBug291().
      • On écrit le test unitaire pour reproduire le bug.
      • On peut ensuite corriger le bug dans le code source.
      • Enfin on relance tous les tests unitaires pour vérifier qu’en corrigeant le bug, il ne réapparaît plus, et que cette correction n’a pas fait survenir d’autres problèmes.
      • Désormais, avant chaque livraison d’une nouvelle version du produit, le bug 291 sera vérifié automatiquement sans qu’aucun développeur n’ait à le faire manuellement.


Faciliter le refactoring

Lorsqu’on travaille dans le développement de logiciels, il faut partir du principe que le changement fait partie des règles du jeu, dans la mesure où tout évolue très vite. Tout peut changer, les usages, les règles métiers, les plates-formes… Il va donc falloir faire appel au refactoring pour faire évoluer le produit au fil de son développement selon les contraintes.

Ces changements sont assez stressants et peuvent vite devenir dangereux pour l’avenir de l’application si l’on a pas de filet de sécurité : c’est le rôle des tests unitaires. Ils permettent au développeur de s’assurer que les fonctionnalités du produit fonctionnent correctement après avoir apporté des modifications au code.

Réduire le stress des développeurs

Quand un développeur veut faire une livraison, plutôt que de lancer l’application et perdre une quantité de temps non négligeable à tester un faible pourcentage de l’application, il se contente de lancer l’ensemble des tests unitaires et en l’espace de quelques secondes, il va pouvoir tester et s’assurer sans le moindre effort que l’application fonctionne bien. C’est donc un gain de productivité et cela permet d’améliorer la qualité et les conditions de travail des développeurs.

Éviter « la matière noire du code »

De nos jours encore, on a parfois l’occasion de rencontrer des procédures de 500 lignes sans le moindre commentaire/tests associés. Ces procédures, tout le monde avait peur de les modifier, par crainte que cela ne fonctionne plus suite à la moindre modification. Avec les tests unitaires, tout le monde peut s’attaquer à n’importe quelle fonction et valider leur bon fonctionnement une par à une.

Cette granularité permet d’avancer pas à pas. Si un test ne passe plus suite à une modification, on peut toujours faire marche arrière, ou alors identifier la nature du problème et le corriger plus rapidement.

Télécharger les sources

Les sources du projet Eclipse pour ce tutoriel sont téléchargeables sur Source Forge à l’adresse suivante.