Test unitaire
CS-108 — Série 1
Introduction
Le but de cette série est de vous entraîner à écrire des tests unitaires, en testant une classe que nous vous fournissons et qui contient quelques erreurs.
La classe à tester — et corriger par la suite — met en œuvre ce qu'on appelle une file bornée d'entiers (bounded integer queue en anglais).
Tout comme un tableau dynamique, une file permet de stocker un nombre variable d'éléments d'un type donné, ici des entiers, mais avec une différence importante : les éléments d'une file sont toujours ajoutés à une de ses extrémités et retirés de l'autre. En cela, une file fonctionne comme une file de caisse de magasin, dans laquelle les nouveaux clients s'ajoutent à une extrémité et en ressortent de l'autre, d'où le nom.
De plus, une file est dite bornée lorsque le nombre d'éléments qu'elle peut contenir est limité. On appelle le nombre maximum d'éléments qu'une file bornée peut contenir sa capacité. Une file qui ne contient aucun élément est dite vide, tandis qu'une file qui en contient autant que sa capacité le permet est dite pleine.
Pour illustrer ces notions au moyen d'un exemple, imaginons que l'on crée une file bornée d'entiers d'une capacité de 5 éléments. Initialement, cette file est vide, ce qu'on peut représenter ainsi, les points d'interrogation désignant la capacité non utilisée de la file :
? | ? | ? | ? | ? |
Ensuite, admettons que l'on ajoute l'entier 2023 à la file. Elle ressemble alors à ceci — si l'on fait l'hypothèse que les éléments sont ajoutés à droite et retirés de la gauche :
2023 | ? | ? | ? | ? |
Ajoutons ensuite l'entier 12. On obtient alors la file suivante :
2023 | 12 | ? | ? | ? |
Finalement, ajoutons les entiers 1, 2 et 3, dans cet ordre. La file est maintenant pleine, et ressemble à ceci :
2023 | 12 | 1 | 2 | 3 |
Si on retire maintenant un élément de cette file, on retire celui qui se trouve à gauche. On obtient alors l'entier 2023, et la file n'est maintenant plus pleine, puisqu'on pourrait lui ajouter un nouvel entier.
12 | 1 | 2 | 3 | ? |
Si l'on ajoute maintenant un dernier entier, disons 50, la file ressemble à ceci :
12 | 1 | 2 | 3 | 50 |
Mise en place
Pour commencer, téléchargez l'archive Zip que nous mettons à votre disposition, extrayez son contenu puis ouvrez le dossier ainsi créé (TSTU
) comme nouveau projet dans IntelliJ. Vous y trouverez les fichiers suivants :
BoundedIntQueue.java
- qui contient l'interface du même nom, représentant une file d'entiers bornée,
queue.jar
- qui contient la version compilée — c.-à-d. un fichier
.class
— d'une classe nomméeBoundedIntQueueBuggy
implémentant l'interface ci-dessus mais contenant quelques erreurs, GetSecretURL.java
- qui contient une classe qui ne sera utile qu'à l'exercice 3.
Une fois le contenu de l'archive importé dans votre projet, dans le panneau Project, faites un clic droit sur le dossier test
et choisissez Mark Directory as puis Test Sources Root.
Ensuite, dans le menu File, choisissez l'entrée Project Structure…, puis cliquez sur Problems dans la partie gauche. Vous devriez voir une ligne mentionnant que la bibliothèque queue
n'est pas utilisée :
1. Library queue is not used [Fix]
Cliquez sur [Fix], choisissez Add to Dependencies… puis OK.
Vous avez maintenant accès à la classe BoundedIntQueueBuggy
qui se trouve dans le fichier queue.jar
, et l'erreur qui était initialement signalée dans GetSecretURL
disparaît donc.
Finalement, ajoutez la dernière version de JUnit à votre projet, en suivant les instructions de notre guide sur le sujet.
Exercice 1
Le but de ce premier exercice est d'identifier les problèmes de la classe BoundedIntQueueBuggy
en écrivant des tests unitaires. Pour créer la classe de test, ouvrez le fichier BoundedIntQueue
, puis dans le menu Navigate, choisissez l'entrée Test puis créez un nouveau test comme suggéré. Prenez garde à placer la nouvelle classe de test dans le paquetage cs108
.
Une fois le squelette de test créé, vous devrez y ajouter des méthodes de test pour les différentes méthodes de la classe BoundedIntQueueBuggy
. Notez que cette dernière possède un unique constructeur qui prend en argument la capacité de la file, sous la forme d'un entier. Ce constructeur devrait lever IllegalArgumentException
si la capacité est inférieure à zéro.
Étant donné que BoundedIntQueueBuggy
implémente l'interface BoundedIntQueue
, lisez les commentaires JavaDoc attachés aux méthodes de cette dernière pour savoir quel est leur comportement attendu.
Pensez à écrire au minimum une méthode de test par méthode de l'interface, et à tester que les exceptions qui devraient être levées le sont bien. Ces tests devraient vous permettre de détecter un certain nombre de problèmes dans la classe fournie, dont vous devez prendre note avant de continuer.
Exercice 2
Écrivez maintenant une classe cs108.BoundedIntQueueOk
, qui implémente l'interface BoundedIntQueue
, mais de manière correcte. Il va de soi que vous pouvez utiliser le test écrit lors de l'exercice précédent pour la tester.
Pour mettre en œuvre la classe BoundedIntQueueOk
, vous avez plusieurs options, plus ou moins simples. Nous vous en proposons trois de difficulté croissante, à vous de choisir celle qui vous convient le mieux :
- Vous pouvez utiliser un tableau dynamique d'entiers, de type
ArrayList<Integer>
, pour stocker les éléments de la file, en utilisant les méthodesadd
etremove
pour ajouter et enlever les éléments. - Vous pouvez utiliser un tableau d'entiers normal, de type
int[]
, pour stocker les éléments de la file. La taille de ce tableau doit être égale à la capacité de la file, et l'élément en tête de file doit toujours se trouver à l'index 0, comme illustré plus haut. Avec une telle représentation, l'opérationremoveFirst
est chère à mettre en œuvre puisqu'elle implique la copie de tous les éléments de la file afin de les décaler d'une position vers le bas. Notez que cette copie peut se faire au moyen d'un simple appel à la méthodearraycopy
de la classeSystem
. - Vous pouvez utiliser une variante de la solution précédente, qui autorise l'élément en tête de file à se trouver n'importe où dans le tableau, et pas uniquement à l'index 0. Attention, cette variante est très efficace mais pas simple à mettre en œuvre !
Quelle que soit la solution que vous choisissez, pensez à placer des assertions dans votre code, aux endroits où cela vous semble judicieux.
Exercice 3
Une fois votre classe BoundedIntQueueOk
écrite et correcte, exécutez le programme GetSecretURL
fourni dans l'archive Zip téléchargée précédemment. Ce programme utilise une file bornée pour calculer et afficher une adresse Web. En l'exécutant sans le modifier, vous devriez voir le message suivant s'afficher :
Téléchargez le code source à cette adresse : https://cs108.epfl.ch/+/uq2o7m/BoundedIntQueueBuggy.java
Malheureusement, l'adresse fournie est invalide car elle a été calculée au moyen de la version incorrecte de la file bornée, BoundedIntQueueBuggy
. Modifiez maintenant la méthode newBoundedIntQueue
de la classe GetSecretURL
pour qu'elle utilise votre mise en œuvre de la file bornée, puis relancez le programme. Si votre mise en œuvre de la file bornée est correcte, vous devriez voir une adresse différente, et en l'entrant dans votre navigateur, vous devriez pouvoir télécharger le code source de notre classe BoundedIntQueueBuggy
.
Si tel est le cas, importez cette classe dans votre projet puis essayez de trouver et de corriger toutes les erreurs qu'elle contient. Pour la tester, utilisez bien entendu votre classe de test ! Notez que cette classe utilise la mise en œuvre rapide, mais plus compliquée, mentionnée à la fin de l'exercice 2.