Thierry Leriche-Dessirier - Architecte du Web
Thierry Leriche-Dessirier - Architecte du Web

Ceci est un simple article d’introduction à JUnit. Cet article sur JUnit n’a pas vocation à faire référence ; il se contente de présenter les notions de base.

Introduction au framework de test JUnit

Désormais plus personne ne remet en cause l’importance des tests dans le cadre du développement logiciel. Ce document s’intéresse plus particulièrement aux tests unitaires. Il existe plusieurs frameworks mais le plus populaire est incontestablement JUnit.

Un univers de standards

L’objectif de cet article est de faire connaissance avec JUnit le plus simplement possible. Or le petit monde de la programmation a déjà adopté un ensemble d’outils et de normes ; autant donc les prendre en compte dès le départ. Parmi les incontournables, nous allons prendre en compte le framework Maven, qui permet de construire les applications et de gérer simplement les dépendances.

Création de l'application avec Maven

La commande suivante est à lancer dans une fenêtre DOS (terminal) sans retour à la ligne :

Cette commande va créer la structure des dossiers nécessaire à la suite de cette démonstration.


Dossiers du projet (Cliquer pour agrandir)

Il faut maintenant demander à Maven de générer les fichiers pour importer le projet dans le logiciel de développement Eclipse.

Ecriture de l'application et des tests avec Eclipse

A ce stade, on en a fini ou presque avec Maven. Il ne reste plus qu’à importer le projet dans Eclipse. Dans Eclipse, choisissez le menu "File / Import", puis l’option "Existing projects into Workspace" et selectionnez le dossier ("thi") du projet dans la popup qui s’ouvre.


Import dans Eclipse (Cliquer pour agrandir)

Le projet apparait dans la zone de gauche d’Eclipse. On reconnait la structure des packages qu’on avait indiquée à Maven.

Premiers tests

Le premier test doit être créé dans la classe "PremierTest" qui doit hériter de "junit.framework.TestCase".

Par convention on a l’habitude de préfixer les Méthodes de test par test. Junit va automatiquement lancer toutes les méthodes identifiées comme des tests.

JUnit propose un ensemble de méthodes pour finaliser les tests. La plus importante et la plus commune est "assertTrue(boolean)" qui valide le test si l’expression booléenne en paramètre est vraie, et l’invalide sinon.

Pour lancer un test, il suffit de faire un clic droit sur le test (ie. la méthode) qu’on veut tester.


Lancer un test (Cliquer pour agrandir)

Le résultat du test s’affiche alors dans la vue JUnit (à gauche sur la capture d’écran) en vert si le test est validé, en rouge sinon.


Résultat du test 1er test (Cliquer pour agrandir)

Plein de tests

En général une classe de test contient de nombreux tests, l’idée sous jacente étant de bien tester chaque fonction de l’application. Eclipse peut au choix lancer les tests indépendamment ou alors tous les tests d’une classe de test d’un seul coup.

Le pendant de assertTrue(boolean) est tout naturellement assertFalse(boolean)


Résultat multi-test (Cliquer pour agrandir)

Automatique

Ce qui est très fort, à ce stade, c’est que Maven va automatiser non seulement la construction de l’application mais aussi le lancement des tests.

Plantage

Pour voir le résultat de JUnit s’afficher en rouge, il suffit de créer un test qui plante. L’instruction 1 == 3 est bien évidement inexacte.

Il convient de noter que chaque test de la classe s’exécute de manière séparée. J’ai volontairement mis le test qui plante au milieu des bons tests pour montrer que ceux d’après se lancent même si un test précédent a échoué. Les tests sont lancés dans l’ordre où ils apparaissent dans la classe.


Résultat d'un plantage (Cliquer pour agrandir)

Cette fois la sortie console de la compillation Maven est un peu différente.

A noter qu’on peut demander à Maven de sauter la phase de test grâce au paramètre "maven.test.skip"

Il existe une forme abrégée bien pratique de cette commande (comme pour de nombreux autres paramètres de Maven) :

La révolution des annotations

La version 4 de JUnit apporte le support des annotations. C’est une petite révolution qui suit la mode actuelle.

Selon votre version de Maven, le projet sera configuré pour JUnit en version 3 ou en version 4. Pour le savoir, il suffit de regarder le fichier "pom.xml". Celui que j’ai utilisé jusqu’à ce stade spécifie "JUnit 3.8.1". Il suffit de remplacer cette valeur en "4.8" (une version récente au moment où j’écris ces lignes) par exemple et de régénérer le projet Eclipse avec la commande "mvn eclipse:eclipse" puis de lancer un "Refresh".

Voici le fichier "pom.xml" après la modification (lignes 14-15)

Grâce aux annotations il n’est plus nécessaire d’hériter de TestCase pour définir une classe de test. Il suffit d’annoter les méthodes avec "@Test". L'import static des méthodes est bien util à ce stade.

Au passage on note qu’il est tout à faire possible d’inclure plusieurs assertions au sein d’un même test comme c’est fait dans la méthode "testDivers()".


Résultat des tests annotés (Cliquer pour agrandir)

Test driven

On peut aussi lancer directement les tests à l’aide de Maven. C’est particulièrement utile dans le cadre du développement dirigé par les tests (test driven)

Ce qui donne, avec notre test "testPlantage()" qui ne marche volontairement toujours pas.

Il est également possible de demander à Eclipse de lancer automatiquement tous les tests (selon des critères à configurer) comme on le fait avec CheckStyle mais je déconseille vivement cette pratique. En effet, bien qu’elle soit séduisante et pleine de sens, elle ne s’applique qu’à des petits projets. Avec l’augmentation du périmètre de l’application, les tests deviennent vite consommateurs et peuvent entraver le travail des développeurs. Il faut laisser l’initiative du lancement des tests locaux aux développeurs. Les utilisateurs d’Eclipse connaissent bien ce point.

setUp et before

Dans de nombreux cas, on a besoin d’initialiser les tests avant de les lancer. JUnit 3 définit la méthode setUp() pour réaliser cela. La méthode setUp() est automatiquement lancée avant chaque test.

S’il y a 4 tests dans la classe alors setUp() sera lancée 4 fois.


Résultat du setUp() (Cliquer pour agrandir)

La version 4 de JUnit, quant à elle, introduit l’annotation "@Before" pour réaliser le même travail. La méthode annotée peut alors prendre n'importe quel nom. Toutefois, on essaie de conserver un nom malin.


Résultat du @before (Cliquer pour agrandir)

Ce qui est encore plus fort, c’est qu’il devient possible d’avoir plusieurs méthodes annotée avec "@Before".


Résultat du @before sur plusieurs méthodes (Cliquer pour agrandir)

Devinez quoi… Le "@Before" aurait dû vous mettre la puce à l’oreille car il existe bel et bien une annotation "@After" indiquant que les méthodes doivent être lancées après chaque test.


Résultat du @After (Cliquer pour agrandir)

Un peu dans la même idée, JUnit 4 propose les annotations @BeforeClass et @AfterClass qui indiquent que les méthodes annotées doivent être lancées avant et après la prise en compte de la classe de test. C’est particulièrement utile quand on doit initialiser une connexion vers la base de données par exemple. En effet ce type d’opération est lourde et couteuse (longue) donc on préfère la réaliser une seule fois pour l’ensemble de la classe au lieu de le faire à chaque test comme ce serait le cas avec @Before.


Résultat du @BeforeClass (Cliquer pour agrandir)

Les exceptions

Dans certains cas, ce que l’on veut vraiment tester, c’est le lancement d’exception quand on sait que le cas d’usage est hors conditions limites. L’annotation "@Test" accepte le paramètre "expected" qui permet justement de spécifier qu’on attend que la méthode sorte en exception.

Dans l’exemple de code, ci-dessus, le test sera validé (en vert donc) si et seulement si la méthode lance une NPE.

Timeout

L’objectif d’un test unitaire n’est absolument pas de mesurer la performance d’une méthode. Toutefois JUnit offre ce type de fonctionnalité. Grâce au paramètre timeout, il est possible d’indiquer un délai limite d’exécution. Le test ne sera validé que si son exécution se déroule en moins de temps que spécifié par le paramètre timeout. Si le temps d’exécution le dépasse alors le test sera invalidé (et donc en rouge dans Eclipse)

En lançant une boucle de concaténation de String (ci-dessus) en tant que test, on est dans un cas de figure où le test peut prendre beaucoup de temps. Cette durée peut mettre le doigt sur un problème d’optimisation. Dans le cas présent, c’est un cas d’école.


Test invalidé car il dépasse 2 secondes (Cliquer pour agrandir)

On notre que Eclipse (colonne de gauche) nous donne les temps d’exécution de chaque test. Ici, on constate que le dernier test s’est effectivement déroulé en plus de 2 secondes.

Tout est dit

JUnit est bien plus complexe et complet que ça mais l’essentiel est dit. Un autre article sera l’occasion d’aller plus loin et de découvrir les fonctionnalités puissances offertes par le framework.

Pour aller plus loin, je vous invite à lire mon article sur le framework EasyMock qui permet d'avoir une vision nouvelle des tests.

Quelques liens

Thierry LER : Architecte du web

Thierry, LERICHE, Architecte Java J2EE, JEE, Thierry, ler, article, JUnit, test, java,

ThierryLer

Avertissement : Vous utilisez Internet Explorer, or ce site est optimisé pour Mozilla Firefox.