Test unitaire

CS-108 — Série 1

Introduction

Le but de cette série est de vous entraîner à écrire des tests unitaires, en en écrivant pour une classe que nous vous fournissons et qui contient quelques erreurs.

La classe que vous devrez tester — et corriger par la suite — met en œuvre ce qu'on appelle une file bornée d'entiers (integer bounded 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 2022 à 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 :

2022 ? ? ? ?

Ajoutons ensuite l'entier 12. On obtient alors la file suivante :

2022 12 ? ? ?

Finalement, ajoutons les entiers 1, 2 et 3, dans cet ordre. La file est maintenant pleine, et ressemble à ceci :

2022 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 2022, 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

Exercice 1

Pour commencer, créez un nouveau projet pour cette série et importez-y le contenu de l'archive Zip que nous mettons à votre disposition, en suivant nos instructions pour IntelliJ ou Eclipse. L'archive contient les trois 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ée BoundedIntQueueBuggy 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, effectuez la manipulation suivante afin de pouvoir utiliser la classe contenue dans le fichier queue.jar :

  • dans la fenêtre Project (si vous utilisez IntelliJ) ou dans l'explorateur de paquetages (Package Explorer, si vous utilisez Eclipse), affichez le contenu du répertoire lib de votre projet, dans lequel le fichier queue.jar a été importé,
  • effectuez un clic droit sur ce fichier, puis dans le menu déroulant qui s'ouvre, sélectionnez l'entrée Add as Library… (dans IntelliJ) ou Build Path > Add to Build Path (dans Eclipse).

Cela fait, l'erreur qui était initialement signalée dans le fichier GetSecretURL devrait disparaître.

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, procédez ainsi :

  • si vous utilisez IntelliJ, ouvrez le fichier BoundedIntQueue, puis dans le menu Navigate, choisissez l'entrée Test puis créez un nouveau test comme suggéré,
  • si vous utilisez Eclipse, ouvrez le menu File, puis New et sélectionnez JUnit Test Case.

Prenez garde à placer la nouvelle classe de test dans le paquetage cs108 et à utiliser la version 5 de JUnit.

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 l'exception IllegalArgumentException si la capacité est inférieure à zéro.

Etant 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

Ecrivez 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 :

  1. Vous pouvez utiliser un tableau dynamique d'entiers, de type ArrayList<Integer>, pour stocker les éléments de la file, en utilisant les méthodes add et remove pour ajouter et enlever les éléments.
  2. 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'indice 0, comme illustré plus haut. Avec une telle représentation, l'opération removeFirst 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éthode arraycopy de la classe System.
  3. 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'indice 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/+/mean94/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.