Vous avez réussi à écrire vos premiers tests unitaires et à comprendre l'importance de la démarche du TDD. Mais pour être sûr que votre code est bien testé et que vos tests sont de qualité, quelques vérifications s'imposent : est-ce que vous avez réalisé suffisamment de tests ? Est-ce que vos développeurs ont bien traité l'étape de refactor (souvenez-vous en TDD : rouge-vert-refactor) afin de garder un code de qualité ?
Java propose de nombreux outils qui permettent de vous aider à effectuer ces types de vérification pour :
maîtriser la notion de couverture de code par vos tests et mettre en pratique avec JaCoCo ;
améliorer la qualité du code avec le formatage, et la détection de code mal écrit, source de bugs potentiels ;
sécuriser votre produit en vérifiant les dépendances de votre code, notamment avec le projet OWASP.
Vous pouvez même intégrer ces vérifications de manière automatisée durant le build, c'est-à-dire l'étape de construction de votre application. C'est l'un des objectifs des outils tels que Maven ou Gradle : fournir un moyen de lancer le build avec l'ensemble des vérifications, grâce à des plugins et un fichier de configuration (le fameux pom.xml pour Maven) !
De ces vérifications, vous pouvez extraire des rapports. Nous allons découvrir dans ce chapitre SonarCloud, qui prend la forme d'une interface web pour développeurs. Êtes-vous prêt à transformer votre écran en cockpit d'avion plein d'indicateurs ?
Pour suivre ce chapitre avec les exemples donnés, vous pouvez vous placer sur la bonne branche du dépôt de code du cours :
git checkout -f p1ch6
Maîtrisez la couverture de code par vos tests
Si vous faites du TDD, vous avez créé au moins un test avant de coder la fonctionnalité, c'est déjà très bien ! Mais au moment de coder la fonctionnalité, vous allez sûrement coder tous les cas d'application de votre fonctionnalité, les exceptions éventuelles, bref, vous coderez sûrement au-delà du périmètre de votre test initial.
Donc, lorsque vous exécuterez les tests, le système ne va pas forcément exécuter toutes les instructions du code de votre fonctionnalité, car les tests n'auront pas forcément prévu tous les cas. Un code est couvert par les tests s'il est exécuté par au moins un test.
Par exemple, avec le calculateur, vous avez codé un test pour tester l'addition de deux entiers. Vous en profitez pour coder l'addition de deux nombres à virgule (type double, ajoutez les lignes 11 à 17 ci-dessous) :
public class Calculator {
public int add(int a, int b) {
return a + b;
}
public int multiply(int a, int b) {
return a * b;
}
public double add(double a, double b) {
return a + b;
}
public double multiply(double a, double b) {
return a * b;
}
...
}
Or, votre test ne s'occupe que des nombres entiers. Le cas de l'addition de deux nombres à virgule de type double n'est pas couvert par les tests, comme l'illustre la figure ci-dessous.
La couverture de tests est en fait la proportion entre la quantité de code couverte par vos tests et la quantité de code totale. Il s'exprime souvent sous la forme d'un pourcentage.
C'est quoi la "quantité" de code ? Et comment je mesure cette proportion ?
C'est une très bonne question, car la quantité de code peut se mesurer de différentes façons. Cela peut conduire à des pourcentages différents ! Sur le comment faire, vous l'avez deviné, vous n'allez pas vérifier à la main le code qui ne serait pas couvert par vos tests ! Des outils existent pour cela, et nous allons découvrir comment le faire avec Eclipse, mais aussi avec Maven.
Les différentes façons de mesurer la quantité de code
Avant de lire ce qui suit, prenez le temps de réfléchir à quel élément vous allez compter, en prenant en compte le fait que cela va servir à mesurer une couverture de code par des tests.
Je vous laisse encore quelques minutes... C'est bon ? 😉
On peut identifier 4 façons principales de mesurer une quantité de code, en comptant :
le nombre de lignes : c'est ce qui semble le plus intuitif. Mais est-ce le plus juste ? Évidemment, les outils de comptage vont éliminer les lignes inutiles, vides, ou de commentaires. Mais il est possible d'avoir des lignes plus complexes que d'autres ;
le nombre d'instructions : pour prendre en compte la complexité d'une ligne, on peut compter toutes les instructions. Mais lorsqu'un développeur code des tests, il étudie les différents cas possibles, y compris les cas d'exception qui doivent être traités avec la même importance, même si les cas d'exception nécessitent souvent moins de code ;
le nombre de branches : au lieu de compter linéairement le code, on va compter le nombre de zones de code par rapport à des conditions. Typiquement, un ensemble d'instructions if/else génère deux zones de code, que l'on appelle aussi branches. Cela permet de prendre en compte à parts égales les différents cas possibles ;
le nombre de fonctions/méthodes : cette fois, on va compter juste le nombre de fonctions/méthodes de l'application. Dès que cette méthode est appelée au moins une fois, le code est indiqué comme couvert, peu importe le nombre de lignes ou de branches parcourues au sein de cette fonction.
Il n'y a pas de méthode de mesure parfaite. Chacune possède ses avantages et ses inconvénients. Parfois, il faudra prendre en compte plusieurs mesures. Confrontez-vous à ces différentes mesures avec les outils que l'on va découvrir.
Si je dois prévoir tous les cas avec mes tests, je dois atteindre 100 % de couverture selon tous les types de mesure ?
Cela peut être très difficile à atteindre. Sur des projets réels, atteindre 70-80 % avec la mesure de votre choix semble être un bon compromis de départ. Au-delà du pourcentage, veillez à prioriser les parties critiques de votre code, à tester au mieux avec des tests unitaires.
Nous verrons dans la troisième partie les tests d'intégration et les tests fonctionnels, qui peuvent aussi compléter cette couverture, pour des parties moins critiques, et tout en respectant la pyramide des tests !
Mesurez la couverture des tests avec Eclipse
Passons à la pratique ! Le paquetage d'installation d'Eclipse fournit un outil de mesure de couverture qui se nomme EclLemma. Pour le tester, c'est très simple, nous allons voir la couverture du code par le test CalculatorTest. Pensez à ajouter les méthodes add
et
multiply pour les types doubles dans la classe Calculator :
public double add(double a, double b) {
return a + b;
}
public double multiply(double a, double b) {
return a * b;
}
Puis, démarrez le test CalculatorTest ; mais au lieu de choisir Run As... sur le menu contextuel, vous allez choisir Coverage As... puis JUnit Test Case comme d'habitude :
Ensuite, votre test va se dérouler comme d'habitude, avec la fenêtre JUnit de résultat. Mais regardez maintenant le code de Calculator, il y a des surlignages verts et rouges :
Dans le code source de Calculator, les lignes sont représentées :
en vert lorsque la classe de tests a parcouru ces lignes ;
en rouge lorsque la classe de tests n'a pas parcouru ces lignes ;
en jaune pour les blocs conditionnels if, while, ou ici try/catch pour lesquels toutes les conditions n'ont pas été parcourues. Ici, il s'agit d'un try/catch, la partie try (ligne 27) est bien parcourue, mais aucune exception n'a été lancée, donc la partie catch (ligne 28) n'est pas parcourue ;
sans surlignage pour les noms de méthodes ou ponctuations ne correspondant pas à du code.
Un nouvel encart s'est aussi ouvert, généralement en bas de l'interface d'Eclipse, il s'agit de l'encart Coverage, indiquant un résultat de couverture :
Vous obtenez différents pourcentages, par rapport au nombre de lignes, ainsi que les quantités de lignes :
un pourcentage total de 95,2 % en nombre de lignes ;
un pourcentage de 80 % pour tout ce qui est dans src/main/java ;
un pourcentage de 100 % pour tout ce qui est dans src/test/java.
Votre code ne contient qu'un test et vous l'avez exécuté, donc le code source du test a été totalement parcouru, c'est normal ! Donc, le pourcentage le plus significatif est celui de src/main/java, et non le pourcentage total, car ce qui vous intéresse, c'est bien le pourcentage du code de l'application !
Nous avons abordé auparavant plusieurs façons de mesurer la couverture des tests. Dans Eclipse, vous pouvez changer la façon de mesurer grâce à l'icône d'option "v" à droite de l'encart Coverage :
Constatez qu'en changeant la méthode de mesure, vos pourcentages changent ! En passant du compteur de lignes au compteur d'instructions, on passe de 95 % à 96,1 % pour le pourcentage total !
Obtenez des rapports de couverture avec Maven et JaCoCo
Insérez l'appel au plugin JaCoCo dans le fichier de configuration pom.xml, dans les balises <plugins> entre les lignes 21 et 22 :
<plugins>
...
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.5</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
Pensez à vérifier sur mvnrepository.com s'il existe une version plus récente du plugin. Cette déclaration permet de déclencher JaCoCo durant la construction des sources et de rédiger un rapport après l'exécution des tests.
Ensuite, en ligne de commandes, lancez la commande :
mvn clean package
À la fin de l'exécution de la commande, vous pouvez voir que JaCoCo a été appelé :
Le rapport se trouve alors dans le dossier target/site/jacoco. Ouvrez dans votre navigateur le fichier index.html, et vous obtenez un rapport cliquable dans le navigateur :
Ce rapport privilégie les mesures instructions et branches, et extrait de fait le code des tests (tout ce qui est dans src/test/java est exclu) pour ne pas polluer les pourcentages obtenus.
Améliorez autrement la qualité de votre code
Dans ce cours, nous nous focalisons essentiellement sur les tests automatisés. Mais n'oublions pas l'objectif de cette pratique : améliorer la qualité et gagner en confiance sur notre produit numérique. Dans ce paragraphe, nous allons aborder un aspect différent : c'est l'amélioration grâce au formatage du code et à l'analyse de code automatisée.
Formatez et nettoyez votre code avec votre IDE
Grâce à votre IDE, vous allez pouvoir améliorer le format de votre code, et surtout le rendre cohérent sur tout votre projet. Mais qui décide de ce qui est souhaitable ? 🤔
C’est une pratique courante des équipes d’avoir des conventions de codage. Elles garantissent la cohérence du code pour des éléments importants comme le placement de l'accolade, ou le nombre d'espaces ou de tabulations pour l'indentation de votre code. Cela peut sembler anecdotique, mais avec peu d'efforts, on peut rendre un code plus lisible, ce qui permet au développeur de se concentrer sur le fond du code, et moins sur sa forme.
Parfois, un mauvais formatage est source de bugs ! Regardez l'exemple ci-dessous :
En Java, on peut écrire une condition if sans accolades ; cela signifie que seule l'instruction suivante sera exécutée (à gauche sur l'illustration). Si, lors d'une évolution, on insère une instruction pour tracer dans un fichier de log une action utilisateur, visuellement, à cause d'une indentation trompeuse, le développeur croit que cela n'a pas d'impact sur son code. Or, sur le code à droite, la méthode transferMoney sera appelée mais si userValid est faux !
Apple a cassé SSL il y a quelques années en oubliant les accolades. Oups. Heureusement, votre IDE est là pour formater votre code et insérer les accolades tout de suite.
Eclipse vous facilite le nettoyage de code de deux façons différentes.
Les options Formatter dans Eclipse
Les options de formatage permettent de changer l'aspect visuel de votre code sans rien changer dans son contenu. Dans le menu Preferences, puis Java - Code Style - Formatter, vous obtenez des options pour choisir un profil de formatage :
Cliquez sur New..., choisissez un nom, et vous entrez alors dans les options de formatage. À gauche se trouvent les options, et à droite une fenêtre de prévisualisation.
Changez quelques options et sauvegardez. Vous pouvez aussi importer un jeu d'options existant, grâce à l'option Import... Je vous propose d'importer mon jeu d'options, qui se trouve à la racine du dépôt de code : Eclipse-Formatter_oc-testing.xml
.
Les options Clean Up dans Eclipse
Les options de nettoyage vont plus loin que le formatage. Elles permettent de rendre la syntaxe du code plus cohérente et changent parfois votre code pour un autre code équivalent ! Dans le menu Preferences, puis Java - Code Style - Clean Up, vous obtenez des options pour choisir un profil de Clean Up, de la même façon que Formatter. Après avoir créé un nouveau profil de Clean Up, vous obtenez aussi une fenêtre d'options, à plusieurs onglets cette fois :
Testez chaque option et regardez leur résultat sur la fenêtre à droite. Cela va vous permettre de consolider vos connaissances en Java, croyez-moi !
De la même façon, vous pouvez importer des options de nettoyage. Testez mon profil qui se trouve à la racine du dépôt :Eclipse-Cleanup_oc-testing.xml
En complément, l'option Java - Editor - Save Actions vous permet d'effectuer des nettoyages à chaque fois que vous sauvegardez ! Ces options permettent d'effectuer le formatage selon le profil de formatage, mais aussi du nettoyage, comme le Clean Up. Cependant, vous devrez repréciser ce que vous voulez nettoyer, indépendamment de votre profil Clean Up.
Analysez automatiquement votre code avec SonarCloud
Bon, votre code est bien plus lisible, c'est déjà une première étape. La deuxième étape ne va pas directement améliorer la qualité de votre code, mais "seulement" vous alerter précisément sur les différents problèmes que peuvent poser certaines parties de votre code. En effet, un code mal écrit peut :
conduire clairement à un bug ;
créer une faille de sécurité ;
rendre le code moins maintenable, toute évolution sera plus difficile et plus coûteuse à coder.
C'est là qu'un outil comme SonarCloud peut vous aider. Il va :
Analyser votre code.
Envoyer son analyse au serveur SonarCloud.
Faire un rapport web dynamique pour lister les problèmes, les prioriser et vous expliquer comment améliorer votre code ! Oui, tout ça !
Pour réaliser tout cela, SonarCloud se base sur des règles qui ont été prédéfinies. Ces dernières ont été créées pour pouvoir émettre un jugement sur des risques potentiels. Cela s’apparente un peu au fait de répondre à un questionnaire en ligne pour vérifier votre santé, mais en plus fiable. SonarCloud possède une vaste base de données des différents types de bugs logiciel connus. Il trouve du code qui fait des actions qui n’étaient probablement pas voulues, qui pourrait causer un risque de sécurité, ou qui ne suit pas les bonnes pratiques.
SonarCloud reprend aussi les rapports de couverture du code par les tests effectués par JaCoCo. En termes de mesure, SonarCloud utilise une mesure qui effectue la moyenne entre le nombre de lignes et le nombre de branches, afin d'obtenir un seul pourcentage "hybride".
SonarCloud propose ses propres serveurs pour mener à bien toutes ces tâches. C'est gratuit pour du code open source, hébergé par exemple sur GitHub. Voici une démonstration de l'utilisation de SonarCloud. Vous aurez besoin :
d'avoir votre propre compte GitHub ;
de savoir forker un dépôt open source, de préférence Java.
Pour créer votre compte GitHub et effectuer un fork, je vous renvoie sur le cours Installez votre environnement de développement Java avec Eclipse.
Forkez le dépôt de ce cours. Puis clonez-le sur votre ordinateur avec la commande :
git clone https://github.com/<votre_identifiant>/oc-testing-java-cours.git
Ensuite, vous allez créer votre compte SonarCloud :
sur la page d'accueil de SonarCloud, cliquez sur l'icône de GitHub en dessous de "Analyze your repo" ;
acceptez la demande d'autorisation pour lier votre compte GitHub et SonarCloud ;
votre compte est créé et vous pouvez créer votre projet SonarCloud :
cliquez sur Create a new project, puis Choose an organization on GitHub ;
acceptez la nouvelle demande d'autorisation de GitHub ;
ne changez rien et cliquez sur Continue et Create Organization ;
vous pouvez alors (enfin !) sélectionner votre projet GitHub et cliquer sur Set Up :
une nouvelle fenêtre vous propose différents outils, cliquez sur Manually ;
sur cette nouvelle fenêtre, sélectionnez Maven et vous obtenez une ligne de commandes Maven pour construire votre projet et envoyer les résultats à Sonar :
copiez ces lignes, mais selon l'invite de commandes que vous utilisez, il vous faudra la modifier pour enlever les caractères "\" et obtenir une ligne du style :
mvn verify sonar:sonar -Dsonar.projectKey=ocstudent-ide_oc-testing-java-cours -Dsonar.organization=ocstudent-ide -Dsonar.host.url=https://sonarcloud.io -Dsonar.login=ee836b16f89559b22237d5720b35b03b92ec402d
exécutez cette commande sur votre nouveau dépôt !
Après exécution de cette commande, et un petit temps d'attente sur SonarCloud, vous allez obtenir enfin votre rapport Sonar !
C'était fastidieux ! Cela en vaut-il vraiment la peine ?
Toutes les étapes d'inscription sont fastidieuses mais ne sont à faire qu'une seule fois. Ensuite, il suffit juste de lancer cette même ligne de commande pour obtenir un rapport à jour.
Une foule d'informations sont disponibles sur cet écran :
des informations sur la branche construite, les nombre de lignes de code, le langage ;
le nombre d'avertissements relevés par thèmes :
Reliability - Bugs : bugs potentiels de votre application,
Security - Vulnerabilities : problèmes potentiels de sécurité,
Maintenability - Code smells : problèmes de syntaxe pouvant rendre votre code moins lisible et moins maintenable (littéralement "mauvaises odeurs de code") ;
la couverture du code par les tests et le nombre de tests unitaires ;
la duplication de code : proportion de code qui semble être du copier-coller. Plus le pourcentage est élevé, moins bonne est la qualité, car hormis les tests, il faut toujours regrouper du code commun et ne pas faire du copier-coller.
Ce cours n'a pas vocation à vous apprendre toutes les ficelles. Je vous laisse découvrir cet outil par vous-même, il est très intuitif. Découvrons cependant ensemble un exemple d'alerte soumise par Sonar.
Cliquez sur le nombre de codes smell. Vous obtenez un écran listant les alertes et sur la gauche des critères pour trier et filtrer ces résultats :
Cliquez sur le premier item de la liste. Vous obtenez alors un extrait de votre propre code source annoté de l'alerte Sonar ! Sur cette alerte, vous pouvez cliquer sur See Rule, et Sonar vous explique la règle et même souvent comment corriger le problème !
Certes, c'est en anglais, mais vous allez faire de très grands progrès en Java si vous vous y mettez et cherchez à comprendre ce qui est écrit.
Sonar vous permet de gérer ces alertes, de les affecter à une autre personne de l'équipe, etc.
Les autres outils de vérification
Surefire et le site Maven
Avec Maven, vous utilisez le plugin Surefire pour exécuter les tests (il est donc activé par défaut dans Maven), mais aussi pour avoir des rapports sur vos tests et générer un rapport HTML. Maven crée un rapport XML quand vos tests échouent, mais peut créer un rapport HTML si vous exécutez :
mvn site
Maven place vos rapports dans target/site/surefire-report.html. Ce n’est peut-être pas le système le plus élégant, mais il vous indiquera les résultats de vos tests.
Plus généralement, la commande mvn site permet de générer un site HTML statique regroupant différentes informations que peuvent générer vos plugins Maven. C'est un outillage plus ancien que SonarCloud en termes de tableau de bord, mais si votre fichier pom.xml déclare beaucoup de plugins liés à des rapports sur votre code, cette commande regroupe le tout dans un site HTML, consultable depuis votre propre machine.
L'audit de sécurité des dépendances
L'outil de OWASP Dependency Checker, qui scanne les vulnérabilités de sécurité. OWASP offre des ressources et conseils de valeur pour construire des applications sécurisées ; cela vaut vraiment le coup d’aller voir le cours OWASP d’OpenClassrooms !
Les autres outils d'audit de code
L’outil Java Checkstyle observe votre code source et vous indique des problèmes de code, principalement liés au formatage de votre code. Par exemple, il permet de vérifier les dénominations de variables et de classes. Il est configurable, et les configurations prédéfinies sont nombreuses. Vous pouvez même commencer par utiliser les styles Java de Google et Oracle.
Les auditeurs de code PMD et FindBugs, plutôt orientés sur des jeux de règles similaires à ceux de SonarCloud.
En résumé
Les outils de build comme Maven et Gradle permettent d'automatiser le lancement des tests mais aussi d'autres vérifications de qualité.
L'étude de la couverture du code par les tests permet de vérifier si vos tests parcourent l'ensemble de votre code. Il existe plusieurs types de mesure.
L'IDE Eclipse permet de vérifier visuellement où vos tests passent. Maven via le plugin JaCoCo permet d'évaluer cette couverture en ligne de commandes.
L'audit automatisé de code permet de vérifier si vous écrivez un code bien construit, qu'il ne produit pas de bugs connus, ni certaines failles de sécurité potentielles, et qu'il est maintenable, c'est-à-dire que d'autres développeurs pourront travailler dessus sans perdre trop de temps.
SonarCloud est un outillage permettant d'effectuer l'audit de code et de l'afficher sous forme de rapports détaillés et interactifs. Il est entièrement gratuit pour les projets open source.
Ouf ! Vous avez déjà beaucoup de tests derrière vous. Vérifiez vos connaissances avec le quiz de la partie 1, et je vous retrouverai dans la prochaine partie pour travailler des techniques de test plus avancées !