đŸ’» TP − DĂ©velopper des tests automatisĂ©s (avancĂ©)

1. Introduction

Ce TP a pour objectif de vous apprendre Ă  configurer et Ă  utiliser le framework de test JUnit sur un projet de code assez rĂ©aliste. En revanche, l’objectif n’est pas d’apprendre Ă  trouver de bons scĂ©narios de test, car cela sera principalement Ă©tudiĂ© en TD.

2. Projet Ă  tester

On vous demande de tester une partie d’une petite bibliothĂšque logicielle destinĂ©e Ă  gĂ©rer un systĂšme de rĂ©servation de voyages en train.

Ce projet fait un usage important de toute la partie de la bibliothÚque standard Java dédiée à la gestion du temps, notamment via les classes Instant, Duration et LocalDate. Il vous sera trÚs utile de consulter la documentation officielle Java qui concerne ces classes.

Quelques conseils :

  • Pour construire un objet de type TicketReservationSystemImpl, il vous faut lui confier un fuseau horaire. Le plus simple est de lui donner celui de votre systĂšme, accessible via : ZoneId.systemDefault().

  • Pour crĂ©er un objet de type Instant, on peut utiliser une mĂ©thode utilitaire fournie dans la bibliothĂšque appelĂ©e TimeManagement.createInstant. Cette mĂ©thode nĂ©cessite elle aussi un fuseau horaire pour fonctionner, et ZoneId.systemDefault() peut faire l’affaire. Par exemple : TimeManagement.createInstant("2021-03-11 11:00", ZoneId.systemDefault()).

  • Pour crĂ©er un objet de type LocalDate, on peut utiliser l’une des variantes de la mĂ©thode utilisataire Java LocalDateTime.of. Par exemple : LocalDateTime.of(2021, Month.MARCH, 11, 11, 0).

3. Travail à réaliser

Avant tout, quelques points importants :

  • Il est possible que vous dĂ©couvriez des dĂ©fauts Ă  l’intĂ©rieur du projet ! Pensez Ă  prendre note des dĂ©fauts dĂ©couverts, et Ă  les corriger au fur et Ă  mesure.

  • Pensez Ă  appliquer les bonnes pratiques Ă©voquĂ©es en cours ! En particulier :

    • Pensez Ă  bien avoir un seul script de test par scĂ©nario de test ! C’est trĂšs important pour faciliter tout travail de diagnostic Ă  partir de votre suite de tests.

    • Chaque script de test doit systĂ©matiquement contenir un oracle qui vĂ©rifie que l’Ă©tat du systĂšme correspond bien Ă  ce qui est attendu. Cet oracle doit ĂȘtre implĂ©mentĂ© Ă  l’aide d’un ou de plusieurs appels Ă  des mĂ©thodes d'assertion fournies par JUnit 5 (se rĂ©fĂ©rer au cours).

3.1. Préparation

  1. Lisez le README du projet pour bien comprendre Ă  quoi sert cette cette bibliothĂšque logicielle et comment on est en censĂ© s’en servir au sein d’un projet de dĂ©veloppement.

  2. RĂ©cupĂ©rez le projet Ă  tester Ă  l’aide d’un git clone.

  3. En vous aidant du cours, ajoutez JUnit 5 comme dépendance Maven du projet.

  4. En vous aidant du cours, préparez le répertoire devant contenir vos tests.

3.2. Test de TrainReservationFactoryImpl

À ce stade, vous avez peut-ĂȘtre constatĂ© que le projet Ă  tester n’est pas trivial et comporte de nombreuses classes et mĂ©thodes. Pour y aller en douceur, nous allons commencer par tester la classe TrainReservationFactoryImpl, dont la spĂ©cification se trouve dans l’interface TrainReservationFactory.

La classe TrainReservationFactoryImpl est ce qu’on appelle une fabrique d’objets. Elle contient des mĂ©thodes createXXX permettant de crĂ©er les instances de base requises lors d’un usage de la bibliothĂšque. Plus prĂ©cisĂ©ment, cette bibliothĂšque a Ă©tĂ© conçue de maniĂšre Ă  ce que toute construction d’objet par un⋅e utilisateur⋅ice doit ĂȘtre accomplie via une instance TrainReservationFactoryImpl.

TrainReservationFactoryImpl n’est pas capable de crĂ©er des instances de toutes les classes disponibles, car des certaines classes ne doivent ĂȘtre instanciĂ©es que par d’autres classes du projet (par exemple un Trip doit forcĂ©ment ĂȘtre créé par un TicketReservationSystem).

Avant toute chose, prĂ©parez une classe de test oĂč vous mettre les scripts de test pour la classe TrainReservationFactoryImpl.

Puis, pour chaque méthode listée ci-dessous de la classe TrainReservationFactoryImpl :

  1. Lisez la spĂ©cification de la mĂ©thode (dans l’interface Java implĂ©mentĂ©e par la classe)

  2. Quel est le domaine d’entrĂ©e de la mĂ©thode ?

  3. Quel est le domaine de sortie de la méthode ?

  4. Implémentez les scripts de test pour les scénarios de test demandés.

Méthodes à considérer :

  • MĂ©thode createCity(String name)

    • Le nom est null,

    • Le nom est vide,

    • L’objet est construit avec succĂšs.

  • MĂ©thode createTrain(String name, int maxPassengers)

    • Le nom est null,

    • Le nom est rempli d’espaces,

    • Le nombre de passagers max est nĂ©gatif,

    • L’objet est construit avec succĂšs.

  • MĂ©thode createTicketReservationSystem(ZoneId timeZone)

    • Le fuseau horaire est null,

    • L’objet est construit avec succĂšs.

3.3. Exécution de la suite de tests

Au fur et Ă  mesure de l’Ă©criture de vos scripts de test, exĂ©cutez-les de deux maniĂšres diffĂ©rentes :

  1. Via Maven en ligne de commande,

  2. Via votre IDE directement (IntelliJ, Visual Studio Code, etc.)

Observez que les deux exĂ©cuteurs de tests vous fournissent bien les mĂȘmes rĂ©sultats, simplement affichĂ©s diffĂ©remment.

3.4. Test de TicketReservationSystemImpl : premiĂšre partie

Le cƓur mĂ©tier de la bibliothĂšque se trouve au sein de la classe TicketReservationSystemImpl. Un objet de type TicketReservationSystem nous permet de dĂ©finir un systĂšme ferroviaire (villes, voyages, etc.), et nous permet d’agir sur ce systĂšme en dĂ©finissant des trajets, des retards, des rĂ©servations ou annulations de billets, etc.

Cette classe permet de se confronter assez fortement avec les difficultĂ©s du test logiciel dans un logiciel orientĂ© objet. Soyez trĂšs vigilants quant aux domaines d’entrĂ©e et de sortie de chaque mĂ©thode, qui sont gĂ©nĂ©ralement plus complexe qu’ils en ont l’air.

Avant toute chose, prĂ©parez une classe de test oĂč vous mettre les scripts de test pour la classe TicketReservationSystemImpl.

Puis, pour chaque méthode listée ci-dessous de la classe TicketReservationSystemImpl :

  1. Lisez la spĂ©cification de la mĂ©thode (dans l’interface Java implĂ©mentĂ©e par la classe)

  2. Quel est le domaine d’entrĂ©e de la mĂ©thode ?

  3. Quel est le domaine de sortie de la méthode ?

  4. Implémentez (et exécutez) les scripts de test pour les scénarios de test demandés.

Méthodes à considérer :

  • MĂ©thode getAllBookedTickets():

    • La liste des tickets est bien initialement vide.

    • La liste des tickets contient bien les tickets ajoutĂ©s Ă  un ou plusieurs voyages.

  • MĂ©thode getAllCancelledTickets()

    • La liste des tickets annulĂ©s est bien initialement vide.

    • La liste des tickets contient bien les tickets annulĂ©s au sein d’un voyage.

  • MĂ©thode findPossibleExchanges(Ticket ticket)

    • Si aucun voyage n’est disponible (Ă  part celui du ticket), rend bien une liste vide.

    • Si un voyage existe et remplit les contraintes, alors rend bien une liste avec ce voyage.

  • MĂ©thode findAvailableTrips(City origin, LocalDate date)

    • Si aucun voyage n’existe Ă  cette date, alors rend bien une liste vide.

    • Si aucun voyage n’existe avec cette ville pour origine, alors rend bien une liste vide.

    • Si un voyage existe avec la bonne ville d’origine et la bonne date, alors rend une liste avec ce voyage.

N’hĂ©sitez pas Ă  vous rĂ©fĂ©rer au cours pour aller relire les diffĂ©rents types d'assertions offertes par JUnit 5 !
Pour le moment on ne se pose pas la question de si ces scĂ©narios de tests sont de bonne qualitĂ©, ou bien s’il sont suffisants pour notre projet. Cela sera traitĂ© dans une autre partie du cours.

3.5. RĂ©-organisation de la suite de tests

Pour vos premiers scĂ©narios de tests, vous avez dĂ» prĂ©parer diffĂ©rents lots de donnĂ©es de test (diffĂ©rents voyages, tickets, villes, etc.). En vous aidant du cours, faites usage de l’annotation @BeforeEach afin de factoriser la prĂ©paration de vos donnĂ©es de test au sein d’une seule mĂ©thode de votre classe de test, et faites ensuite usage de ces donnĂ©es factorisĂ©es au sein de vos diffĂ©rentes scripts de test.

Puis, faites usage de @Nested pour créer une classe interne de test pour chaque méthode testée.

Si vous avez déjà fait bon usage de @BeforeEach et @Nested pour votre suite de tests initiale, ignorez cette question.

3.6. Test de TicketReservationSystemImpl : deuxiĂšme partie

Toujours pour TicketReservationSystemImpl, pour chaque méthode listée ci-dessous :

  1. Quel est le domaine d’entrĂ©e de la mĂ©thode ?

  2. Quel est le domaine de sortie de la méthode ?

  3. Implémentez (et exécutez) les scénarios de test demandés.

Méthodes à considérer :

  • MĂ©thode cancelTrip(Trip trip)

    • Un voyage qui n’Ă©tait pas dĂ©jĂ  annulĂ© devient bien annulĂ© (Ă  la fois du point de vue de l’objet Trip et de l’objet TicketReservationSystem)

    • Annuler un voyage dĂ©jĂ  annulĂ© ne fait rien.

  • MĂ©thode createTrip(City origin, City destination, Train train, Instant departure, Instant arrival):

    • Si le nouveau trajet a lieu avant le dernier trajet du train, alors une exception TripException est renvoyĂ©e.

    • Si l’origine et la destination sont identiques, une exception TripException est renvoyĂ©e.

    • Si le dernier trajet enregistrĂ© pour le train a pour destination une ville diffĂ©rente de la ville d’origine du nouveau trajet, alors une exception TripException est renvoyĂ©e.

    • Si toutes les contraintes sont respectĂ©es, alors (a) un objet Trip est bien créé avec toutes les informations demandĂ©es, et (b) cet objet est bien ajoutĂ© Ă  l’ensemble des voyages enregistrĂ©es dans le TicketReservationSystem

  • MĂ©thode delayTripDeparture(Trip trip, Duration delay)

    • Étant donnĂ© un voyage valide et un dĂ©lai non nul, un dĂ©lai est bien ajoutĂ© Ă  la fois pour le dĂ©part et l’arrivĂ©e d’un train.

  • MĂ©thode delayTripArrival(Trip trip, Duration delay)

    • Étant donnĂ© un voyage valide bien enregistrĂ© dans le systĂšme, un dĂ©lai est bien ajoutĂ© pour l’arrivĂ©e du train.

    • Étant donnĂ© deux voyages valides, bien enregistrĂ©s dans le systĂšme, avec le mĂȘme train, un dĂ©lai ajoutĂ© au 1er train est bien rĂ©percutĂ© sur le dĂ©part du 2e train pour l’arrivĂ©e du train.

À nouveau, n’hĂ©sitez pas Ă  vous rĂ©fĂ©rer au cours pour aller relire les diffĂ©rents types d’assertions offertes par JUnit 5, notamment en ce qui concerne les exceptions !

3.7. (Optionnel) Continuer

N’hĂ©sitez pas Ă  vous entraĂźner en continuant d’enrichir la suite de tests comme bon vous semble ! Toutes les mĂ©thodes sont bien documentĂ©es, donc intuitivement vous pourrez trouver pas mal de choses Ă  tester.

Mais notez qu’il faudra attendre le chapitre sur les approches de test pour aller au delĂ  de l’intuition et apprendre Ă  rigoureusement sĂ©lectionner les scĂ©narios de test nĂ©cessaires 😃.