TP autour de test et de l'utilisation du debugger dans Eclipse

1 Premier projet avec Eclipse

Le travail en java à l'aide de l'environnement Eclipse est centré autour de la notion de projet. Un projet désigne l'ensemble de constituants d'une application : fichiers source, manière de les compiler, manière d'exécuter les programmes associés. Eclipse utilise son propre espace pour stocker les fichier associés à un projet. Généralement, cet espace se trouve dans le répertoire eclipse-workspace situé dans le répertoire de login (ou dans Documents sous Windows).

Pour commencer à travailler sur un ensemble de fichiers source en java il faut commencer par créer un projet :
File->New Project->Java->Java Project
Nommez votre projet et validez. Ce projet apparaît alors dans la partie gauche de la fenêtre, il faut alors le compléter avec les fichier de départ que nous utiliserons dans le TP. Pour premier TP, nous partirons des fichiers de l'archive TP.zip. Pour cela, nous devons choisir l'une des trois possibilités suivantes :

  • File->Import->General->Archive File permet d'importer le contenu d'une archive (zip typiquement) au projet. On prendra soin de placer les fichier importés dans le répertoire src du projet (dédié aux fichiers source). Avec cette méthode, les fichiers sont copiés dans le sous répertoire approprié de eclipse-workspace ;
  • Aller dans les propriétés du projet, puis dans la rubrique Java Build Path et Link source, puis choisissez un répertoire ainsi qu'un nom associé dans Eclipse. Avec cette méthode, les fichiers présents dans le répertoire choisi sont directement modifiés par Eclipse ;
  • Aller dans le répertoire eclipse-workspace/MonProjet/src et placer les fichiers ici. Avec cette méthode on obtient un résultat équivalent à la première méthode. A noter qu'il faudra parfois demander à Eclipse de rafraîchir son affichage avec File->Refresh pour voir les fichiers.

Les fichiers donnés du TP du jour sont maintenant présents dans le paquetage par défaut, visible dans le répertoire où nous avons choisi de placer nos fichiers. Vous pouvez alors double cliquer sur l'un des fichiers pour éditer sont contenu. Les gains immédiats obtenus avec Eclipse sont des facilités de lecture et d'écriture :

  • possibilité de masquer ou rendre visible le corps des méthodes (à l'aide du petit + ou - présent à gauche de la signature ;
  • complétion automatique en cours de saisie : un menu s'affiche pendant la saisie, il est alors possible de continuer la saisie normalement sans autre incidence ou alors de selectionner une option proposée dans le menu pour que le texte associé s'écrive. Il est possible de provoquer l'affichage de ce menu avec Ctrl-Espace ;
  • Indication à la volée des erreurs de syntaxe, soulignées en rouge, un explication détaillée apparaît en laissant la souris sur l'erreur ou en appuyant sur F2 avec le curseur sur l'erreur.

2 Premiers pas avec le Debugger

Aujourd'hui, nous vous fournissons un fichier Liste.java qui ne fonctionne pas, il est truffé de bugs. Le but est de corriger ce fichier, non pas en lisant le code et en cherchant au hasard les erreurs, mais en utilisant deux outils : les tests et le debugger. Commencez par lire le fichier TestListe.java qui contient un programme de test pour nos listes constitué de méthodes qui nous aiderons à vérifier la cohérence de nos résultat (tete, queue, taille, contient, triee), de fonctions permettant d'exécuter chacune des méthodes de Liste, d'afficher le résultat et de vérifier sa cohérence (partiellement), ainsi que d'un programme principal qui lance des séries de tests.

Pour exécuter ce programme il vous suffit de cliquer sur la flèche verte de l'interface d'Eclipse. Celui-ci devrait éventuellement vous demander quelle est la classe principale de votre projet (TestListe) avant de l'exécuter. Alternativement, vous pouvez selectionner le fichier principal et selectionner Run->Run as->Java Application.

Le programme s'exécute alors et vous pouvez remarquer que les résultats sont incorrects. Il semble que les tests de sfcohérence (instructions assert) ne sont pas exécutés. En effet, assert est une instruction spéciale de java qui est, en temps normal, ignorée et ne génère aucun surcoût. Pour l'activer il faut le demander à la machine virtuelle : allez dans Run->Run configurations sélectionnez TestListe et dans l'onglet Arguments ajoutez -ea dans la partie VM arguments. Exécutez, vous pouvez alors constater que, dès que l'expression donnée à un assert est fausse, l'exécution est stoppée par une exception indiquant la position de l'erreur.

Nous allons commencer par lancer le debugger pour observer l'exécution incorrecte. La première méthode contenant une assertion invalide est insereTete. Placez un point d'arrêt au début de cette fonction en double cliquant dans la colonne tout à gauche de la fenêtre d'édition (une petite pastille bleue apparaît alors à cet endroit). Lancez alors l'exécution avec le debugger à l'aide du bouton en forme d'insecte : Eclipse vous propose de changer de perspective, acceptez, vous pouvez alors constater que l'organisation des fenêtres change afin d'être plus adaptée à l'activité de debuggage. Le programme s'exécute alors et s'arrête au point d'arrêt que vous avez placé. Un fenêtre affiche le contenu des variables et la ligne en cours (pas encore exécutée) est surlignée.

Avancez de ligne en ligne jusqu'à l.insererTete(element) à l'aide du bouton représentant une flèche sautant au dessus d'un point (Step Over). Vous pouvez remarquer que le programme s'exécute ligne par ligne en mettant à jour l'affichage et les variables. Une fois arrivés à l.insererTete(element), nous souhaitons maintenant poursuivre l'exécution pas à pas à l'intérieur de cette méthode. Pour cela utilisez le bouton en forme de flèche qui pointe entre deux points (Step Into), vous arrivez alors dans la méthode insererTete de la classe Liste.

Dans la fenêtre consacrée aux variables, vous pouvez étendre this en cliquant sur le triangle. Vous pouvez alors voir la valeur des attributs de l'objet courant. Continuez l'exécution ligne par ligne et observez la valeur de ces attributs ainsi que de ceux de l'objet pointé par nouvelle. Vous devriez alors constater que tete est mis à jour avec une mauvaise valeur. Corrigez le problème. Une fois le problème corrigé, vous pouvez changer de perspective (pour revenir à la perspective java) à l'aide du groupe de boutons situé en haut à droite.

3 Nous étoffons nos techniques

Pour la suite de l'exécution, vous pouvez constater que l'exécution tombe sur une boucle infinie après suppression de la tête avant même les tests de cohérence. Lancez l'exécution à l'aide du debugger et, durant la boucle infinie, interrompez la à l'aide du bouton pause. Le debugger arrête le programme dans une partie qui ne vous concerne pas forcément (typiquement à l'intérieur des méthodes d'affichage), sélectionnez alors dans la pile d'appels (fenêtre en haut à gauche) la partie du code qui vous intéresse (Liste.afficher()). Continuez un peu l'exécution ligne par ligne en observant la variable courant, vous pouvez constater que la boucle est bien infinie car courant.suiv est égal à courant. Reprenez alors la méthode précédente à base de point d'arrêt pour exécuter pas à pas la méthode supprimeTete de Liste, trouvez l'erreur et corrigez la.

Maintenant, si vous n'avez pas trop bien corrigé les précédents bugs, nous devrions avoir une nouvelle erreur qui ne se voit pas à l'affichage : l'insertion en queue de 3 dans une liste vide donne la liste avec pour seul élément 3, mais l'une des assertions échoue. Si vous mettez un point d'arrêt sur queue, et vous suivez son exécution vous devriez en trouver la raison et pouvoir corriger les deux méthodes incriminées. Cela souligne l'importance des tests qui peuvent couvrir plus de choses que ne le fait un simple affichage.

Pour le bug suivant, il faut attendre plusieurs insertions en queue avant que le bug ne survienne. Plus précisément, c'est à la troisième itération qu'une assertion est violée. Pour le trouver, nous pouvons mettre un point d'arrêt dans la boucle qui appelle insère queue, laisser le programme s'arrêter, lui dire de continuer (bouton avec une barre et un triangle accolés) deux fois et exécuter pas à pas à partir du troisième arrêt sur le point d'arrêt. Mieux, nous pouvons rendre le point d'arrêt conditionnel : dans la perspective de debuggage, cliquez sur l'onglet Breakpoints, sur votre point d'arrêt, puis cochez conditionnal et saisissez une condition (ici i==2). Le programme s'arrête maintenant directement à la bonne itération ! Continuez l'exécution pas à pas et trouvez le bug. Come variante, vous pouvez vous arrêter dans insereQueue à la ligne appelant l.insererQueue(element) lorsque l'élément vaut la valeur insérée au moment du bug (7).

4 Test insuffisant

Pour le prochain les tests ne suffisent pas. Le bug se trouve dans l'insertion en place mais n'est pas detecté et n'est pas visible sur l'affichage. La méthode de test employée ici ne couvre simplement pas suffisament de cas. Pour faire apparaître le bug, essayez d'insérer 3 entiers dans tous les ordres possibles. Par exemple, avec 1, 2, et 3, essayez d'insérer 1 puis 2 puis 3, videz la liste, essayez 1 puis 3 puis 2, videz la liste essayez 2 puis 1 puis 3, et ainsi de suite. Le bug devrait alors apparaître. Corrigez le.

5 Conclusion

Pour terminer, utilisez toutes les techniques vues jusqu'alors pour corriger le dernier bug dans la fonction supprime.