LOG4500 Travail Pratique 1 Tests unitaires John Mullins Pascal Bachand 20 septembre 2006 1 Introduction 1.1 Mise en situation Vous personnifiez deux jeunes brillant(e)s entrepreneur(e)s ayant fondé une entreprise sur le point de faire l offre initiale d action sur le marché. Vous avez jusqu ici acquis une bonne réputation en recyclant des travaux pratiques de Poly à des clients qui n y ont vu que du feu... Il vous reste un dernier contrat avant d aller faire les gros bidoux sur le plancher de la bourse. Quelqu un, quelque part à la direction de l UdM, subissant probablement la pression des bonzes l UNESCO (institut de la statistique), a pris la décision de remplacer les ascenseurs du Pavillon Principal. Le contracteur responsable des travaux a fait appel à votre organisation pour programmer le contrôleur de l ascenseur. Comme vous n avez aucune expérience avec ce genre de système et que votre réussite aura une incidence majeure sur la valeur (virtuelle) de votre entreprise, vous décider de construire une simulation en Java afin de tester la conception de votre contrôleur. 1.2 Objectifs généraux Vous avez récupéré une spécification en langue naturelle d un contrôleur d ascenseur sur la Toile et l avez adapter à votre projet de simulateur (section 4 4 Spécification). Vous planifiez effectuer les tests en trois étapes : TP1 : Développement et tests unitaires pour tester le comportement local (section 4.1 Comportement local). TP2 : Instrumentation du code et couverture TP3 : Suite de tests fonctionnels et d intégration pour tester le comportement global (section 4.2 Comportement global). De plus, afin d épater le parterre, vous planifier de démontrer que vous n avez aucune crainte d utiliser des méthodes plus systématiques et que, conséquemment, votre organisation se démarque franchement des autres appelés du marché : TP4 : Vérification automatique avec SPIN TP5 : Méthode déductive
2 TP1 : Développement du simulateur et tests unitaires Le simulateur est développé en Java. Un objet Simulateur se charge d établir la communication entre les différents objets (Porte, Usager, Ascenseur). 2.1 Simplifications Vous décidez d y aller avec une première itération du simulateur o`u les simplifications suivantes s appliquent : Un seul ascenseur Deux usagers au même étage doivent demander l ascenseur pour la même direction. Dans le cas contraire, un usager attend que l autre utilise l ascenseur. 2.2 Code et modélisation UML Vous disposez d un début d implantation et d une modélisation du système en UML (Figure 1). Figure 1. Modélisation UML
2.3 Exemple de trace Si vous avez implanté correctement le comportement local décrit dans la spécification (section 4.1 Comportement local), vous devriez obtenir un trace d exécution similaire à la suivante (avec le préambule établit initialement dans Simulateur) : Début de la simulation 1. # Usager[0]: # effectue l'appel 2-UP 2. # Usager[1]: # effectue l'appel 3-DOWN 3. + Ascenseur: + direction: UP 4. + Ascenseur: + étage: 2 5. + Ascenseur: + arrêt a l'étage 2 6. * Porte étage 2: * ouverture 7. # Usager[0]: # entre dans l'ascenseur 8. # Usager[0]: # entre la destination 3 9. * Porte étage 2: * fermeture 10. + Ascenseur: + fin de l'arrêt 11. + Ascenseur: + direction: UP 12. + Ascenseur: + étage: 3 13. + Ascenseur: + arrêt a l'étage 3 14. * Porte étage 3: * ouverture 15. # Usager[0]: # destination atteinte 16. # Usager[1]: # entre dans l'ascenseur 17. # Usager[1]: # entre la destination 1 18. * Porte étage 3: * fermeture 19. + Ascenseur: + fin de l'arrêt 20. + Ascenseur: + direction: DOWN 21. + Ascenseur: + étage: 2 22. + Ascenseur: + direction: DOWN 23. + Ascenseur: + étage: 1 24. + Ascenseur: + arrêt a l'étage 1 25. * Porte étage 1: * ouverture 26. # Usager[1]: # destination atteinte Fin de la simulation 2.4 Tests unitaires Les tests unitaires visent à valider le comportement d une composante d un logiciel. En programmation OO, une composante consiste généralement en une classe. Au cours de ce TP vous aurez à tester les classes Porte, Ascenseur et Usager et pour ce faire, vous devrez instrumenter le code de manière à ce que ces classes soit bel et bien testées de manière indépendante. Pour cela vous aurez à prendre en considération les concepts de Stubs et Drivers. Dans notre design, c est la classe Simulateur qui démarre les objets et gère les signaux entre eux, c est pourquoi la classe de TestSimulateur hérite de Simulateur et l emploi en tant que driver. Vous pouvez donc utiliser ses attributs et ses signaux pour gérer le flot de vos tests.
Les autres classes qui ne sont pas testées agiront en tant que stubs et ne seront donc pas actives; vous devez donc vous assurer que leur comportement n est pas démarré par la méthode «run()». Vous pouvez par contre créer des instances de ces classes et modifier leurs attributs de manière à modifier l état du système 3 Outils Vous disposez des outils suivants. 3.1 Scripts d exécution Trois scripts sont fournis avec le code source :./compile.sh qui permet de faire la compilation du code.java en classes exécutables.class;./run.sh qui permet d exécuter le simulateur; et./test.sh qui permet d exécuter vos tests unitaire JUnit. 3.2 eclipse Éditeur graphique Java ainsi qu un environnement de compilation et de tests. Nous vous suggérons d utiliser cet outil de programmation, mais vous pouvez aussi utilisez tout autre éditeur ainsi que les scripts fournis si nécessaire. Si vous utilisez eclipse pour la première fois, lisez le tutoriel : http://www.info.polymtl.ca/~log4500/demarrer_eclipse.html qui vous guidera pour l ouverture d un projet, ainsi que l exécution du simulateur et de vos tests. 3.3 librairie JUnit Cette librairie Java offre des classes et méthodes qui vous permettront de systématiser vos tests unitaires et de les exécuter à la chaîne à partir de la ligne de commande (script./test.sh) ou d une interface graphique (via eclipse). http://www.junit.org/index.htm 3.4 argouml Éditeur de graphes UML open source. http://argouml.tigris.org/ 3.5 Hansel Cet outil vous permet de systématiser certains tests de couverture. http://hansel.sourceforge.net/ 3.6 SPIN Cet outil permet une vérification automatique du comportement global d une modélisation du système. http://spinroot.com/spin/whatispin.html
4 Spécifications 4.1 Comportement local 4.1.1 Les portes Variables étage : l étage de la porte [de 1 à nb. d étages] Comportement [1] Attendre que l ascenseur soit à l arrêt à l étage de la porte [2] Ouvrir la porte [3] Attendre un certain laps de temps [3 sec.] [4] Fermer la porte [5] Signaler à l ascenseur qu il peut redémarrer 4.1.2 Les usagers Variables étage : l étage courant de l usager direction : la direction que l usager veut emprunter destination : la destination de l usager Comportement [1] Tant que la porte est close ou la direction de l ascenseur incorrecte : [1.1] Si aucun appel n est signalé, l usager effectue un appel [1.2] Attendre que la porte s ouvre [1.3] Décider d entrer ou non (l usager peut être distrait) [1.4] Si la porte est toujours ouverte, entrer dans l ascenseur [2] Signaler la destination [3] Attendre que l ascenseur soit à destination et que la porte s ouvre [4] Sortir et terminer 4.1.3 L ascenseur Variables étage : l étage courant de l ascenseur direction : la direction courante de l ascenseur destinations : un vecteur des destinations entrées par les usagers appels : un vecteur des appels effectués par les usagers Comportement [1] Si l ascenseur n a aucune direction, mais qu il existe un appel à l étage, prendre la direction de cet appel. [2] Ouvrir la porte si nécessaire [2.1] Signaler l arrêt [2.2] Attendre la fermeture de la porte
[2.3] Effacer l appel ou la destination pour l appel courant [3] Choisir la direction : [3.1] S il existe une destination au dessus, changer la direction vers le haut (respectivement, vers le bas, ou à l étage (none)) [3.2] Sinon, [3.2.1] Si la direction de l ascenseur est vers le haut ou none : 3.2.1.1.S il existe un appel au dessus, garder la direction vers le haut. 3.2.1.2.S il existe un appel en dessous changer la direction vers le bas. 3.2.1.3.Sinon rester à l étage (none). [3.2.2] Si la direction de l ascenseur est vers le bas : 3.2.2.1.S il existe un appel en dessous, garder la direction vers le bas. 3.2.2.2.S il existe un appel au dessus changer la direction vers le haut. 3.2.2.3.Sinon rester à l étage (none). [4] Monter ou descendre d un étage selon la direction [5] Renverser la direction si l ascenseur atteint l étage le plus haut (resp. le plus bas) 4.2 Comportement global 1. Lorsque l ascenseur est en mouvement, aucune porte n est ouverte. 2. Il est toujours vrai qu un usager qui demande l ascenseur y entrera fatalement 3. Il n y a jamais plus d une porte d ouverte à la fois. 4. La distance parcourue par un usager est toujours égale à source destination. 5 Travail à faire 5.1 À faire 1. Nous vous fournissons une implantation incomplète du Simulateur d ascenseur. Vous devez terminez sont implantation en respectant les spécifications fournis, et plus particulièrement la méthode «choisir_direction()» de la classe Ascenceur.java. 2. Utilisez la variable de DEBUG pour structurer vos sorties à l écran durant votre implémentation et déboguage. Implémentez aussi les méthodes «tostring()» pour les classes Ascenseur et Usager, que vous pourrez utilisez durant le déboguage ainsi que vos tests unitaires. 3. Comme premier test d intégration, vous devez reproduire la trace d exécution décrite à la section 2.3 Exemple de trace par la classe Simulateur en maintenant le préambule tel qu il est implanté dans la méthode «runsimulateur()». 4. Dessiner les diagrammes de flot pour la méthode Usager ::run(), la méthode Ascenseur ::choisirdirection(), et la méthode Ascenseur ::run(). 5. Pour vous familiariser avec la librairie JUnit, vous devrez exécuter des tests unitaires pour les 3 classes du comportement local. Nous vous fournissons le travail pour la classe Porte; aidez-vous de cet exemple pour produire deux tests unitaires de votre cru pour la classe Usager et la classe Ascenseur. La qualité ou le pourcentage de couverture de vos tests ne seront pas évalué, mais la lisibilité du
code et clarté lors de l exécution du test le sera, ainsi que votre utilisation/compréhension de la librairie JUnit. 6. Identifier sur vos diagrammes de flots, la position des assertions de vos 4 tests unitaires. 7. Répondre aux questions de discussion 5.2 À Remettre Vous devez remettre une archive (tar czvf nom_archive fichers_à_archiver) comprenant les fichiers de votre implantation ainsi qu un rapport comprenant : 5.2.1 Résultats Des extraits de code pertinents montrant que vous avez implanté correctement le comportement local (méthode Ascenseur ::choisisrdirection()). Votre trace d exécution du Simulateur Des extraits de codes montrant l implantation de vos tests unitaires La trace d exécution de vos tests unitaires Vos 3 diagrammes de flots avec l emplacement des asserts de vos tests unitaires 5.2.2 Discussion Nous demandons une très courte réponse à vos questions (1-3 lignes seulement)! 1. Pour vos 4 tests unitaires, faites une courte description de la motivation derrière votre choix de test, et/ou du comportement que vous vouliez vérifier. 2. Quelle est la motivation de faire des tests unitaires? 3. Quelle est la motivation de structurer ses tests dans une classe telle que TestSimulateur? 4. Quelles seraient les prochaines étapes? 5. Comment évaluer la qualité de vos tests?