Objectifs:

  • découvrir l'automatisation des tests unitaires en Java avec les frameworks JUnit et JaCoCo (Java Code Coverage).
  • découvrir l'outil de construction de projets (build tool) Maven de la fondation Apache.

Quelques liens utiles pour compléter ce TP :

1. Créer un projet Java avec Maven sous VSCode

Maven est un outil logiciel qui vous aide à gérer des projets Java et à automatiser la création d'applications. L'extension Maven pour Java pour Visual Studio Code (intégrée au Java Extension Pack) fournit une prise en charge Maven entièrement intégrée. Cette extension vous permet :

  • de générer des projets Maven à partir d'archétypes Maven,
  • d'explorer des projets Maven,
  • d'exécuter des commandes Maven (objectifs (goals) communs, objectifs de plugins, commandes personnalisées),
  • de conserver l'historique des commandes pour une réexécution rapide.

Pour découvrir l'utilisation de Maven sous VSCode, vous allez construire un projet simple que vous enrichirez dans les exercices suivants pour effectuer des tests unitaires avec Junit5. Les instructions suivantes détaillent les différentes étapes de cet exercice, que vous pouvez aussi retrouver dans une vidéo de 17min. : Créer un projet Maven avec VSCode

  1. Placez vous dans votre répertoire de travail PLAI/Java
  2. Créez un répertoire TP06
  3. Positionnez vous dans le répertoire TP06.
  4. Lancez VSCode

  5. Dans la vue explorateur
    1. effectuez un clic droit
    2. dans le menu contextuel, sélectionnez l'item Create Maven Project

    Une alternative est de procéder comme suit

    1. Ouvrir la palette de commandes :(CTRL+Shift+P)
    2. Taper java et dans la liste des commandes proposées choisir Java:Create Java Project...
    3. Dans les différentes manières de créer un projet Java choisir Maven create from archetype
  6. Choisissez l'archétype No Archetype qui vous permettra de créer directement un projet Java Maven avec une structure simple que vous pourrez facilement compléter pour la suite.
  7. Donnez ensuite un groupId à votre projet, ici fr.im2ag.m2cci.
  8. Rentrez ensuite l'artifactId de votre projet, ici compteur.
  9. VSCode vous demande d'indiquer un dossier (Folder) où ranger ce projet, sélectionnez le répertoire TP06.
  10. Une fenêtre d'alerte vous indiquant la création du projet est affichée, cliquez sur le bouton OK
  11. Le projet apparaît dans votre explorateur, ouvrez le fichier Main.java qui a été créé dans le dossier src du projet compteur. Une fois le fichier ouvert vous devriez avoir les éléments suivants dans votre fenêtre VSCode (cela peut prendre un certain temps, le temps que l'extension Java analyse le code).
  12. Ouvrez le fichier pom.xml, vous pouvez constater que le projet est configuré pour fonctionner avec une version 1.8 de Java (propriétés maven.compiler.source et maven.compiler.target). Remplacez les numéros de version dans les par le numéro de version de votre JDK (si vous ne le connaissez pas, faites la commande java -version dans un terminal sur votre machine).
  13. Après l'avoir modifié, sauvegardez votre fichier pom.xml. L'assistant Maven de VSCode vous demande alors si vous voulez ou non mettre à jour le classpath de votre projet pour tenir compte de ces modifications
  14. Dans la vue Java Projects
    1. faites un clic droit sur le package {} fr.im2ag.m2cci
    2. dans le menu contextuel sélectionnez l'item New
    3. dans le sous menu sélectionnez l'item Package
    4. saisissez le nom du nouveau package (fr.im2ag.m2cci.compteur)
  15. De la même manière, dans le package fr.im2ag.m2cci.compteur créez une nouvelle classe Java : Counter
  16. Modifiez le code source de cette classe, en le remplaçant par le code de Counter.java qui modélise un compteur entier (pour plus d'informations sur le fonctionnement de cette classe étudiez attentivement sa javadoc).
  17. Modifiez le programme principal situé dans la classe Main afin de créer deux compteurs et d'afficher leur somme.
  18. Exécutez ce programme et vérifiez que la somme affichée correspond bien à la valeur attendue.
    1. Dans l'application Main cliquez sur le bouton Run
    2. Dans le terminal, vérifiez que l'affichage produit est bien celui de la capture d'écran ci-dessous

2. Créer des tests unitaires JUnit avec Visual Studio Code

L'extension Java pour VSCode propose un support intégré pour la génération et l'exécution de tests unitaires à l'aide du framework JUnit (www.junit.org). Pour expérimenter avec cette fonctionnalité vous allez écrire les tests unitaires de la classe Counter vue dans l'exercice 1.

2.1 Configurer le projet pour utiliser JUnit5

Par défaut, la construction du projet avec sans archétype a généré un projet non configuré pour des tests effectués avec JUnit. Pour pouvoir utiliser JUnit5 (la dernière version du framework) il faut modifier le fichier de configuration du projet pom.xml.

Les instructions suivantes détaillent les différentes étapes de cet configuration

  1. Ouvrez le fichier pom.xml et rajoutez une dépendance vers la version de JUnit que vous souhaitez utiliser pour le projet (5.10.1 au 9 janvier 2024) dans un élément <code>dependencies</code> que vous insérerez après l'élément <code>properties</code>
    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.10.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
  2. Rajouter une dépendance à la dernière version du plugin maven-surefire-plugin en charge de l'exécution des tests (3.2.3 au 9 janvier 2024). Pour cela, rajoutez après l'élément dependencies un élément build avec le contenu suivant
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.2.3</version>
            </plugin>
        </plugins>
    </build>
  3. Sauvegardez le fichier pom.xml et si VSCode vous le demande, synchronisez votre classpath pour prendre en consideration les modifications que vous venez d'effectuer.

2.2 Générer une classe de tests unitaires

Vous allez maintenant générer le code de la classe de test associée à la classe Counter.

  1. Effectuez un clic droit dans la fenêtre d'édition de Counter.java et sélectionnez l'éléments Source Action... dans le menu contextuel.
  2. Dans le sous menu qui apparait choisissez l'item Generate Tests...
  3. Spécifiez ensuite le nom de la classe de test générée (CounterTest).
  4. Demandez ensuite la génération des cas de tests pour chacune des méthodes de la classe Counter en cochant toutes les méthodes.

Dans son organisation standard pour les projets Java, Maven sépare le code source des classes de votre application (répertoire src/main/java) du code source des classes de test (répertoire test/src/java).

Pour chacune des méthodes de la classe Counter une méthode de test correspondante a été produite dans le fichier CounterTest.java. Pour le moment toutes les méthodes de test (méthodes précédées de l'annotation @Test) ont un corps vide, vous devrez bien entendu compléter ce code par la suite pour y ajouter vos tests.

2.3 Exécuter les tests unitaires

L'exécution des test unitaires peut se faire depuis un terminal en tapant la commande mvn test (Attention il faut se placer dans le répertoire compteur).

Il est egalement possible de le faire en utilisant les extensions Java de VSCode. Pour cela vous pouvez procéder comme suit :

  1. Dans la barre d'outils sélectionnez l'outil testing qui permet d'afficher la vue Testing qui regroupe les différentes classes de test de votre projet et pour chacune d'elles les différents cas de test proposés.
  2. Dans cette vue, sélectionnez la classe CounterTest et cliquez sur le bouton Run Test associé

L'exécution de toutes les méthodes de test de la classe CounterTest est lancée

Vous pouvez constatez que tous les tests passent avec succès, ce qui est normal toutes les méthodes de test ne comportant aucune instruction.

2.4 Ecriture des cas de test

Exercice: complétez la classe CounterTest pour que chacune des fonctionnalités de la classe Counter soit testée.

  1. Commencez par écrire une méthode de test pour l'addition en remplaçant le code généré par :

    @Test
    public void testAdd() {
        System.out.println("add");
        Counter c1 = new Counter(10);
        Counter c2 = new Counter(4);
        Counter c3 = c1.add(c2);
        assertEquals(14, c3.getCount());
        assertEquals(10, c1.getCount());
        assertEquals(4, c2.getCount());
    }
  2. Pour toutes les autres méthodes de test, faites en sortent qu'elle échouent en ajoutant une instruction fail(); dans leur corps, comme dans l'exemple ci-dessous.
    @Test
    void testDecrement() {
        fail("test pas encore implémenté");
    }
  3. Relancez les tests de la classe et vérifiez que le cas de test testAdd passe alors que tous les autres cas de test échouent.

    Modification de la classe CompteurTest et réexécution des tests.
  4. Ecrivez le code pour les autres cas de test (vous pouvez vour référer à la javadoc de la classe Assertions de JUnit afin de voir les différents types d'assertions possibles). La classe Counter contient des bugs; découvrez les avec les tests unitaires, corrigez les et vérifiez que TOUS vos tests réussissent.

3. Ajout d'un outil de couverture de code

Une fois que tous les cas de test ont été exécutés avec succès pour la classe CounterTest, vous vous allez pouvoir ajouter à votre environnement de développement un outil de couverture de code: JaCoCo (Java CodeCoverage).

3.1 Declaration du plugin JaCoCo dans le fichier pom.xml

Un plugin maven (jacoco-maven-plugin) permet d'utiliser l'outil de converture de code lors de l'exécution des tests du projet maven d'application java. Afin de l'utiliser, il vous faut à nouveau modifier le fichier pom.xml qui définit la configuration de votre projet.

  1. Ouvrez le fichier pom.xml,à la fin de celui-ci, entre la balise fermante </pluginManagement> et la balise fermante </build> ajoutez un élément plugins pour intégrer le plugin maven dédié à jaCoCo :

    le code à insérer

    <plugin>
        <groupId>org.jacoco</groupId>
        <artifactId>jacoco-maven-plugin</artifactId>
        <version>0.8.11</version> <!-- dernière version du plugin au 09/01/2024 -->
        <executions>
            <execution>
                <id>default-prepare-agent</id>
                <goals>
                    <goal>prepare-agent</goal>
                </goals>
            </execution>
            <execution>
                <id>jacoco-report</id>
                <phase>test</phase>
                <goals>
                    <goal>report</goal>
                </goals>
            </execution>
        </executions>
    </plugin>
  2. Sauvegardez le fichier pom.xml et mettez à jour le classpath et la configuration de votre application.

3.2 Lancement des tests avec couverture de code

Une fois le plugin jacoco-maven-plugin installé vous pourrez lancer les tests avec un calcul de couverture de code en passant par Maven.

  1. Avant de lancer les tests avec JaCoCo rajoutez à votre classe Counter la méthode suivante :

        /**
         * Création d'un nouveau compteur dont la valeur est le produit entre la
         * valeur de ce compteur et celle du compteur passé en paramètre
         *
         * @param c le compteur à soustraire à ce compteur
         * @return le compteur créé
         */
        public Counter mult(Counter c) {
            return new Counter(this.count * c.count);
        }
  2. Dans la vue Maven sélectionnez et exécutez la cible (goal) clean pour nettoyer votre projet, c'est dire supprimer tous les fichiers générés par la compilation du projet et l'exécution des tests

  3. Faites de même pour exécuter la cible test qui lancera les tests en les instrumentant avec JaCoCo.
  4. Les tests sont lancés et JaCoCoverage instrumente la machine virtuelle Java, pour mesurer la couverture de code puis une fois les test terminé génère un rapport de tests. Vous pouvez voir cela dans le terminal où s'affiche la trace d'exécution de la phase test.

  5. Vous pouvez accéder aux rapports de couverture du code de votre projet en ouvrant dans un navigateur le fichier index.html situé dans le sous répertoire site/jacoco du dossier target (dont le contenu est généré par Maven).

3.2 Ajouter l'extension Coverage Gutters à VSCode

Vous pouvez aussi visualisez la couverture de code directement dans l'éditeur de VSCode pour cela, il vous faut installer une extension capable d'exploiter les données récupérées par JaCoCo, par exemple l'extension Coverage Gutters

le site web de Coverage Gutters
Page web de l'extension Coverage Gutters sur VSCode Market Place

Une fois l'extension Coverage Gutters installée (et les tests exécutés par Maven)s

  1. Ouvrez le fichier Counter.java dans l'éditeur de VSCode
  2. Cliquez sur le bouton Watch pour observer la couverture de code sur cette classe
  3. Vous pouvez alors voir les instructions qui ont été exécutées pendant le test, et celles qui ne l'ont pas été ainsi que le taux de couverture de code de vos tests.
  4. En cliquant à nouveau sur le bouton Watch, vous pouvez masquer la couverture de code dans l'éditeur.

Exercice : assurez-vous que les tests unitaires de la classe Counter offrent une totale couverture de code.

4. Ecriture des tests unitaires de la classe Rational

Exercice: Ecrivez le code JUnit5 permettant de tester la classe Rational vue dans le TP 4. Bien entendu, vous vérifierez que vos tests unitaires assurent une totale couverture de code.

Pour cela procédez comme dans l'exemple précédent

  1. créez un second projet maven (rationals) dans lequel vous recopierez la classe Rational,
  2. modifiez le fichier pom.xml (version de JDK, dépendances JUnit5, mise à jour du n° de version de maven-surfire-plugin, ajout du plugin pour jaCoCo...),
  3. générez la classe de test (RationalTest), et faites en sorte que tous les tests échoue (en mettant un assertion fail() dans chacun des cas de test (dommage que le générateur de tests ne le fasse pas par défaut).
  4. écrivez chaque méthode de test et vérifiez que le test passe (ne pas écrire tous les tests puis tester, mais effectuez les tests au fure et à mesure que vous écrivez les tests),
  5. lorsque tous les tests sont réussis, vérifiez la couverture du code.

5. TDD Test Driven Developpment

Dans cet exercice il s'agit d'adopter une méthode dirigée par les tests (Test Driven Development) pour réaliser une classe qui répresente des comptes bancaires.

4.1. "Cahier des charges"

Il s'agit de définir une (des) classe(s) JAVA permettant de modéliser des comptes bancaires.

  • Un compte bancaire est identifié par un numéro de compte. Ce numéro de compte est un entier positif permettant de désigner et distinguer sans ambiguïté possible chaque compte géré par l'établissement bancaire. Chaque compte possède donc un numéro unique. Ce numéro est attribué par la banque à l'ouverture du compte et ne peut être modifié par la suite. Dans le code de la classe Compte et des tests que vous allez écrire, vous ne vous préoccuperez pas de la manière dont ce numéro est obtenu ni si il est unique : vous créerez des objets compte en fournissant un numéro arbitraire, le code de la classe compte ne se souciant pas de l'unicité de ce numéro (cela sera le problème du programme utilisant les comptes).

  • Un compte est associé à une personne titulaire du compte, cette personne étant décrite par son nom, son prénom et son adresse. Une fois le compte créé, le titulaire du compte ne peut plus être modifié. Une même personne peut être titulaire de plusieurs comptes.

  • La somme d'argent disponible sur un compte est exprimée en Euros. Cette somme est désignée sous le terme de solde du compte. Ce solde est un nombre décimal qui peut être positif, nul ou négatif.

  • Le solde d'un compte peut être éventuellement (et temporairement) être négatif. Dans ce cas, on dit que le compte est à découvert. Le decouvert d'un compte est nul si le solde du compte est positif ou nul, il est égal à la valeur absolue du solde si ce dernier est négatif.

  • En aucun cas le solde d'un compte ne peut être inférieur à une valeur fixée pour ce compte. Cette valeur est définie comme étant - (moins) le découvert maximal autorisé pour ce compte. Par exemple pour un compte dont le découvert maximal autorisé est 2000 €, le solde ne pourra pas être inférieur à -2000 €. Le découvert maximal autorisé peut varier d'un compte à un autre, il est fixé arbitrairement par la banque à la création du compte et peut être ensuite révisé selon les modifications des revenus du titulaire du compte.

  • Créditer un compte consiste à ajouter un montant positif au solde du compte.

  • Débiter un compte consiste à retirer un montant positif au solde du compte. Le solde résultant ne doit en aucun cas être inférieur au découvert maximal autorisé pour ce compte.

  • Lors d'une opération de retrait, un compte ne peut être débité d'un montant supérieur à une valeur désignée sous le terme de débit maximal autorisé. Comme le découvert maximal autorisé, le débit maximal autorisé peut varier d'un compte à un autre et est fixé arbitrairement par la banque à la création du compte. Il peut être ensuite révisé selon les modifications des revenus du titulaire du compte.

  • Effectuer un virement consiste à débiter un compte au profit d'un autre compte qui sera crédité du montant du débit.

  • Lors de la création d'un compte seul l'identité du titulaire du compte est indispensable. En l'absence de dépot initial le solde est fixé à 0. Les valeurs par défaut pour le découvert maximal autorisé et le débit maximal autorisé sont respectivement de 800 € et 1000 €. Il est éventuellement possible d'attribuer d'autres valeurs à ces caractéristiques du compte lors de sa création.

  • Toutes les informations concernant un compte peuvent être consultées : numéro du compte, identité du titulaire, montant du découvert maximal autorisé, montant du débit maximal autorisé, situation du compte (est-il à découvert ?), montant du débit autorisé (fonction du solde courant et du débit maximal autorisé).

4.2. Travail demandé

Il vous est démandé de définir et tester une classe (Compte) qui doit permettre à une application de créer et utiliser autant de comptes bancaires que nécessaires, chaque compte étant un objet, instance (ou exemplaire) de la classe Compte.

Question 1 : A partir du "cahier des charges" précédent élaborer une spécification d'une classe Java modélisant un compte bancaire.

Il s'agira en analysant le texte ci-dessus de :

  • définir les attributs (variables d'instance, variables de classe) de la classe Compte,

  • d'identifier les méthodes publiques proposées par la classe Compte. Pour chaque méthode on prendra soin, outre la définition de sa signature, de spécifier son comportement sous la forme d'un commentaire documentant.

  • de proposer un ou plusieurs constructeurs pour la classe Compte. Là aussi on complétera la donnée de la signature de chaque constructeur avec un commentaire documentant détaillant son utilisation.

Ecrivez le squelette de la classe Compte en mettant dans le corps de chaque constructeur ou méthode une instruction

throw new UnsupportedOperationException("Opération pas encore implémentée);

indications pour la classe Compte:

  • le numéro de compte est attribué à la création d'un compte, il doit donc apparaître comme paramètre du(des) constructeur(s), une fois l'objet Compte créé il ne peut ensuite être modifié.
  • lorsqu'une opération illégale est effectuée (par exemple débiter un compte avec un montant négatif), procédez comme pour le TP 4 (Nombres rationels), levez une exception de type IllegalArgumentException

Question 2 : Une fois les spécifications de votre classe Compte effectuées écrivez un programme JUnit pour ses tests unitaires

  1. générez le code du programme JUnit pour les tests untitaires,
  2. écrivez les différents cas de test.

Question 3 : Réalisez l'implémentation de la classe Compte et au fur et à mesure que vous complétez le code vérifiez que le code que vous avez écrit passe les tests

Question 4 : Une fois l'implémentation de Compte terminée, vérifiez la couverture de vos tests. Si le code de votre classe n'est pas couvert en totalité complétez les tests.

6. Pour aller plus loin

Si vous voulez en faire un peu plus, écrivez des test unitaires JUnit5 pour la classe Ensemble de Lettre vue au TP5 (si vous le l'avez pas faites une correction est disponible)