Skip to topic | Skip to bottom
Home
Minfo05
Minfo05.GlooTD4r1.8 - 18 Oct 2006 - 23:00 - PhilippeCollettopic end

Start of topic | Skip to actions

TD 4 : JML et JUnit

PhilippeCollet

Point de départ

  • Afin que JBuilder et les autres outils (ant, jml) soient tolérants vis-à-vis des accents, modifiez la variable d'environnement LANG pour qu'elle ait la valeur fr_FR@euro :
   export LANG=fr_FR@euro
  • Idéalement faites cette modification dans le fichier de configuration de votre shell.
  • Créez un nouveau projet dans JBuilder dans un répertoire fait pour ce 4ème TD.
  • Téléchargez l'archive jmlbebetes.zip et décompactez la dans le répertoire de votre projet (l'archive contient un répertoire src, un répertoire test, mais aussi des fichiers qui doivent aller à la racine du projet).
  • Rafraichissez les fichiers du projet dans JBuilder, vous devriez voir apparaitre les nouveaux packages et classes.

Vous êtes encore face à une nouvelle version du simulateur de bébêtes, qui tente de répondre à une partie des tests demandés dans le TD3 :

  • Des cas de test ont été créés pour la méthode effectueDeplacement() afin de détecter et de corriger trois erreurs :
    1. la largeur et la hauteur ont été confondues dans une affectation.
    2. Les modifications sur x et y ne sont pas faites indépendamment (si x doit être modifié, y ne l'était pas, ce qui est gênant dans les coins).
    3. L'opérateur % n'est pas vraiment modulo, c'est le reste de la division entière, donc si la directionCourante devient négative, elle n'est pas modifiée...
  • Le code a donc été modifié dans la méthode effectueDeplacement() et tous les tests fournis passent.

Pour exécuter ce code, il faut encore que notre projet, tout neuf, explicite qu'il a besoin de la bibliothèque de JUnit, junit.jar. Dans le TD3, le fait de passer par les assistants de création de tests avait automatisé cette tâche...

  • A l'intérieur de JBuilder, il faut référencer les jars à l'aide du concept de bibliothèque. Dans le menu Tools, lancez "Configure Libraries". Une bibliothèque est une référence vers un ensemble de classes (.class) ou de jar afin de manipuler plus facilement les dépendances d'un projet envers une bibliothèque (ou les dépendances entre bibliothèques). Les bibliothèques sont gérées selon trois points de vue :
    • Project : pour des bibliothèques propres à un projet.
    • User home : pour des bibliothèques vues par tous les projets de l'utilisateur.
    • JBuilder : pour des bibliothèques vues par tous les utilisateurs (intéressant pour personnaliser JBuilder dans une entité de développement, mais il faut avoir les droits pour ça...).
  • Fermez la configuration de bibliothèques et allez dans les propriétés du projet. Dans le noeud "Path", l'onglet "Required Libraries" est normalement vide. Ajoutez (Add...) la bibliothèque JUnit.

Recréez deux configuration d'exécution (comme dans le TD précédent) :

  • un lanceur classique de l'application
  • un lanceur de test. Vous pouvez faire une donfiguration d'exécution de test générique en cochant, dans la partie "Run", un package au lieu d'une classe individuelle. Sélectionnez le package bebetes et voila que JBuilder va exécuter automatiquement toutes les méthodes de toutes les classes de test du package !

Testez tout ça.

Spécifications en JML

Vous pouvez aussi remarquer que certaines parties de classes contiennent quelques spécifications JML (des invariants, quelques pré et postconditions). On va maintenant s'aider de ces spécifications JML pour tester, de différentes manières, certains aspects du simulateur de bébêtes.

Configurer votre environnement pour JML

Les outils JML peuvent être utilisés comme des commandes, mais on va les utiliser a travers ant. Vous devez avoir dans votre répertoire un fichier build.xml et les deux fichiers de propriétés. Des cibles et des propriétés ont été ajoutées pour piloter les outils JML. Les hypothèses sur l'environnement sont légèrement modifiées et un répertoire lib à la racine du projet peut maintenant contenir des bibliothèques nécessaires aux projets. Dans le fichier default.properties, des propriétés définissent les différentes bibliothèques nécessaires.

Il faut maintenant configurer votre environnement pour pouvoir utiliser les outils JML :

  • Créez un répertoire lib à la racine de votre projet.
  • Créez des liens symboliques de tous les fichiers du répertoire ~collet/minfo/TD4/lib/ vers le répertoire lib de votre projet.
  • On va aussi référencer jml-release.jar dans une nouvelle bibliothèque dans la partie "Project". A partir du menu "Tools/Configure Libraries", créez cette bibliothèque (bouton New, puis donnez lui un nom et ajoutez le jar par Add...).
  • Ajoutez ensuite cette nouvelle bibliothèque dans les prérequis du projet, comme pour JUnit.
  • Toujours dans le panneau des chemins du projet, sélectionner le noeud "Ant" dans le sous-arbre "Build", et ajoutez la bibliothèque dans la liste des bibliothèques contenant des tâches ant (cela va permettra d'utiliser des tâches ant spécifiques à JML à travers JBuilder).
  • Intégrer maintenant le fichier build.xml dans votre projet.
  • En dépliant la petite fourmi dans le menu de gauche, vous voyez apparaître toutes les cibles. Les cibles intéressantes pour JML sont :
    • compileJML : qui compile tous les fichiers Java pour produire un bytecode JML+java.
    • fullcheckJML : qui vérifie uniquement la cohérence des spécifications JML dans les fichiers sources. Par défaut, cette tâche ant vérifie tous les aspects des spécifications JML, et notamment les clauses assignable qui n'ont pas été décrites dans le projet. En revanche, la tâche compileJML ne fait que le minimum de vérification, c'est celle-ci que vous utiliserez.
    • runJML : qui lance l'application en activant les assertions.
    • runJMLtest : qui lance des tests avec les assertions JML activées.
    • jmlunit-gui : qui permet de lancer l'interface graphique pour la génération de tests à partir de spécifications JML.

Exécution avec les assertions JML

A l'intérieur de JBuilder, lancez la tâche ant "runJML" qui va tout compiler (car elle dépend de "compileJML") et lancer l'application avec les assertions activées.

  • Que constatez-vous ?
  • Il faut donc corriger un certain nombres d'erreur dans le code. Faites-le jusqu'a ce qu'aucune assertion ne soit violée à l'exécution.

Il manque un invariant dans la classe BebeteAbstraite pour contraindre la direction courante. Ajoutez le, puis relancer...

  • Continuez a corriger...

Ici, on n'a vraiment fait des tests unitaires, mais plutot un gros test d'intégration avec les 10 bébêtes qui se balladent au hasard dans le champ.

Exécution de tests avec les assertions JML

A l'intérieur de JBuilder, lancez la tâche ant "runJMLtest" qui va lancer tous les tests unitaires avec les assertions activées:

  • Que constatez-vous ?
  • Jetez un coup d'oeil a la racine du projet. Il y a normalement un fichier de résumé par classe de test. Ce sera utile pour investiger par la suite...

A l'aide de l'assistant de création de TestCase, créez un nouvelle classe pour tester le constructeur de ChampDeBebetes. Créez justement un champ de 9 pixels de large et 9 pixels de haut avec 10 bébêtes.

  • Relancez les tests.
  • Trouvez quelle assertion est violée.
  • La source de l'erreur est-elle vraiment dans la classe incriminée ?
  • Placez une ou plusieurs assertions supplémentaires pour contraindre le champ par rapport à la vitesse max.
  • Retestez.
  • Si vous avez bien fait le boulot, le test échoue toujours mais pas au même endroit. C'est normalement votre assertion qui détecte le problème. Il faudrait donc que le test passe, ou du moins, qu'il ne soit pas significatif... Pour cela, entourez votre appel au constructeur par le code suivante
       try {
            //@todo : mettre le code du constructeur de champ
            /** Pas de code de test, ce sont les assertions qui vont faire le travail d'oracle */
        } catch (org.jmlspecs.jmlrac.runtime.JMLEntryPreconditionError e) {
            // devrait être comptabilisé comme "sans signification"
        } catch (org.jmlspecs.jmlrac.runtime.JMLAssertionError e) {
            // vraie erreur, les postconditions ou invariants ne sont pas satisfaits
            fail(e.getMessage());
        }
  • Retestez et vérifier que tout va bien maintenant !

Génération de tests à partir des spécifications

A l'intérieur de JBuilder, lancez la tâche ant "jmlunit-gui". Une interface graphique devrait se lancer :

  • Ajoutez la classe BebeteHasard dans la liste.
  • Décochez l'option "quiet" dans le panneau inférieur, afin de visualiser que la génération va bien se passer.
  • Appuyez sur la flèche pour générer les classes de test correspondantes.
  • Regardez les classes générées dans JBuilder. C'est gros et plutôt compliqué, il y a de la plomberie de tests unitaires, du code à étendre, des itérateurs qui appellent des stratégies.
  • C'est tellement compliqué qu'on a du mal a faire recompiler tout ça par JML lui-même. Ce n'est pas encore complètement au point...
  • Juste pour vous donner une idée, exécuter rapidement dans Jbuilder (pas ant) la classe générée pour les tests (celle dont le nom se termine par Test), en cliquant avec le bouton droit sur son nom (dans la vue projet), et en faisant "run using ..." en utilisant bien votre lanceur d'appli (pas celui des tests).
  • Vous devriez voir apparaître quelque chose comme 226 tests passés. Ce qu'il faut savoir :
    • le résultat n'est pas significatif car les assertions ne sont pas activées...
    • les cas de tests sont alimentés par des itérateurs qui tournent sur chaque type utilisé en paramètre dans chaque méthode de la classe : pour BebeteAbstraite, il y en a déjà beaucoup rien que pour le constructeur.
    • l'alimentation des données se fait par des stratégies fournies pour chaque type. Par défaut, sur des types de base, il y a des valeurs significatives (-1, 0, 1 pour les entiers...)...
    • Tout ça est composé par un produit cartésien, d'où un nombre rapidement élevé de tests.
    • Bien évidemment, il y a plein de tests incohérents (donc non significatifs), car les valeurs ne sont pas le domaine du type de données (les invariants sont violés avant d'appeler la méthode testée, ou les préconditions de la méthode sont violées). C'est uniquement quand les postconditions sont violées qu'un test échoue effectivement. JMLunit utilise donc le même genre de "try catch" que dans la question précédente pour séparer les tests non signicatifs des échecs !

Pour ceux qui auraient fini (?!)

  • Rajoutez des assertions dans les classes des bébêtes et du champ, testez...
  • Ecrivez des tests, testez...

-- PhilippeCollet - 23 Oct 2005
to top


You are here: Minfo05 > GlooTD4

to top

Copyright © 1999-2017 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding WIKIDeptinfo? Send feedback