Maximize Page
Tech & DevOps HubEspace Tech & DevOps: Explorez le monde du Dev, du Cloud et des outils DevOps à travers nos articles et discussions Explore the world of development, the cloud and DevOps tools

Couverture de code avec JaCoCo - toute branche

Date de l'article:10-10-2025
Github-Actions Spring Boot SonarCloud JaCoCo
Configuration de JaCoCo pour définir des seuils progressifs par branche (develop/staging/main) et ainsi que l'intégration avec SonarCloud Free Plan. Nous verrons aussi les limites du plan gratuit et la stratégie "Clean as You Code"

L'objectif est de clarifier ce que mesure réellement la couverture de code, ainsi que ses limites.
Nous mettons en place "JaCoCo", configuré avec la génération de rapports (HTML/XML) et nous définissons le seuils de couverture en pilotant des exigences différentes, selon les branches via GitHub Actions. Enfin nous verrons comment intégrer "SonarCloud" tout en restant compatible avec les contraintes du plan gratuit

Essentiel dans une chaîne DevOps, l'intégration de JaCoCo (Java Code Coverage) dans un pipeline CI/CD répond à un objectif principal:

Mesurer et garantir la qualité du code de manière automatisée

Mesure de l'efficacité des tests

JaCoCo analyse quelles sont les parties du code source qui sont réellement exécutées lors des tests (unitaires, d'intégration ou E2E)

→ Il génère ensuite des rapports détaillés montrant les lignes couvertes (vert), partiellement couvertes (jaune) ou non testées (rouge)
Mise en place de "Quality Gates"
Dans un pipeline (Jenkins, GitHub Actions, GitLab CI), on peut définir des seuils de couverture (exemple: 80% minimum); → Si un changement fait descendre la couverture sous ce seuil, le pipeline échoue et bloque le déploiement
Détection précoce des risques
En identifiant les zones "sombres" du code (non testées), JaCoCo aide à :

Réduire les bugs en production
Diminuer la dette technique
Vérifier les validations

Flexibilité et intégration
Contrairement à d'autres outils, JaCoCo utilise un agent Java qui instrumente le bytecode à la volée.

→ Il s'intègre naturellement avec SonarQube et d'autres outils d'analyse.

dev report
Tests vs Couverture: deux notions différentes

La distinction est subtile mais importante: les tests sont l'action de vérifier, tandis que la couverture est la mesure de cette action

Les Tests (l'action)

Sont des scénarios créés par les développeurs pour valider que le logiciel fonctionne comme prévu. L'objectif est de détecter des bugs, valider des fonctionnalités ou des exigences métier

La Couverture de code

La Mesure (calculée par des outils comme JaCoCo) est une métrique quantitative. Elle mesure le pourcentage de lignes, de branches ou d'instructions exécutées par les tests

Attention: 100% de couverture ne veut pas dire "zéro bug"
→ Cela indique juste que chaque ligne a été exécutée, non qu'elle ai été vérifiée avec de bonnes assertions, de bonnes données ou dans tous les contextes

Et voici pourquoi il ne faut jamais viser "100%" de couverture comme objectif ultime
Mesure de présence, pas de pertinence
La couverture nous dit qu'une ligne a été exécutée, pas que le test a vérifié le bon comportement.
→ Un test sans assertions peut “couvrir” 100 % du code sans détecter de bug
L'illusion de sécurité
Une couverture élevée peut cacher des tests médiocres: On peut exercer une fonction complexe avec des données trop simples et ignorer les cas limites, alors que ce sont souvent eux qui cassent en production.
La loi des rendements décroissants

Passer de 0 % à 70 % de couverture apporte énormément de valeur.
Passer de 95 % à 100 % coûte très cher pour un gain de fiabilité souvent faible.

→ À ce stade, on se met parfois à écrire des tests fragiles ou artificiels juste pour faire plaisir au compteur de couverture
La métrique vs La logique

La couverture suit la structure du code (les lignes).
Elle n'a aucune idée des fonctionnalités manquantes.

→ On peut avoir 100 % sur le code existant et pourtant oublier complètement un cas d'usage métier important
En résumé:
→ La couverture est un très bon indicateur de risque et un détecteur de zones non testées, mais une mauvaise mesure de la fiabilité réelle d'une application.

Concevoir des seuils progressifs (Ratchet / Clean as You Code)
Passer d'un projet sans métriques à une CI stricte peut être un choc pour l'équipe
Pourquoi un seuil unique est une mauvaise idée
Imposer un seuil global fixe (par exemple 80%) sur un projet existant est souvent contre-productif.
→ Si le projet est à 20%, l'objectif semble inatteignable, ce qui décourage l'équipe et incite parfois à écrire des “tests vides”, juste pour faire monter la couverture.
Pour éviter cela, on va progresser par étape et seuil progressifs, par environement.
Principes de seuils progressifs (Ratchet Effect)

L'idée n'est pas d'atteindre 80% partout, tout de suite, mais de ne jamais régresser

Seuil sur le "Nouveau Code"
On accepte une couverture globale faible (par exemple 30 %), mais on exige 80 % sur les lignes modifiées (diff coverage)
Ce principe est au cœur de la méthodologie "Clean as You Code" de SonarQube/SonarCloud


Clean as You Code & Effet cliquet (Ratched)
Clean as You Code: On impose des standards élevés sur le nouveau code, ce qui permet d'améliorer progressivement le legacy sans tout réécrire
Effet cliquet: Dès que la couverture globale monte (par exemple de 40 à 45%), on fixe le nouveau seuil; à 45%, et on s'interdit de redescendre
En combinant les deux, la couverture devient un garde-fou progressif, pas un objectif impossible à atteindre
On peut aussi appliquer la règle du Boy Scout : "Laisse le code plus propre que tu ne l'as trouvé"
Chaque bug ou nouvelle feature est une occasion d'augmenter la couverture autour de la classe concernée

Seuils recommandés par branche (vérité CI/CD)

Des seuils progressifs sont appliqués via la CI, afin d'illustrer une approche réaliste en contexte professionnel

Branche Seuil Mode Objectif
develop - non-bloquant Feedback uniquement: Informer, habituer l'équipe, ne jamais casser le flux
staging 70% bloquant Qualité projet: éviter la régression et stabiliser le socle
main 80% bloquant + SonarQube Gate finale: standard “enterprise” et utilisation de métrique officielle
On peut résumer ainsi: develop = expérimentation staging = validation technique main = engagement qualité

Configuration Maven JaCoCo (local & CI)
Intégration de jacoco-maven-plugin et sonar-maven-plugin
Retrouver le pom complet dans le repository de l'application Flashcard
1. Définition des propriétés (en haut du pom.xml)
<properties>
  <jacoco.plugin.version>0.8.11</jacoco.plugin.version>
  <jacoco.check.skip>false</jacoco.check.skip>
  <jacoco.minimum.coverage>0.0</jacoco.minimum.coverage>
  //..
  <sonar.plugin.version>4.0.0.4121</sonar.plugin.version>
  <sonar.projectKey>val7304_flashcards</sonar.projectKey>
  <sonar.organization>val7304</sonar.organization>
  <sonar.projectName>flashcards</sonar.projectName>
  <sonar.coverage.exclusions>
      **/FlashcardsApplication.java,
      **/*Configuration.java,
      **/*Config.java,
      **/dto/**
  </sonar.coverage.exclusions>
  <sonar.coverage.jacoco.xmlReportPaths>${project.build.directory}/site/jacoco/jacoco.xml</sonar.coverage.jacoco.xmlReportPaths>
</properties>

<jacoco.minimum.coverage> sera surchargé par les pipelines (develop / staging / main)


2. Plugin & prepare-agent
<!-- JaCoCo -->
<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>${jacoco.plugin.version}</version>
    <executions>
        <!-- active agent -->
        <execution>
            <id>prepare-agent</id>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
        </execution>
        //...  

Le plugin JaCoCo Maven doit attacher l'agent pendant les tests (prepare-agent)

De là, il va générer les rapports HTML/XML (point3)

Il peut faire échouer le build si la couverture minimale n'est pas atteinte (check avec COVEREDRATIO au point5)


3. Les rapports
<!-- HTML/XML -->
<execution>
    <id>report</id>
    <phase>verify</phase>
    <goals>
        <goal>report</goal>
    </goals>
</execution>
4. La vérification de couverture
<!-- check -->
<execution>
    <id>jacoco-check</id>
    <phase>verify</phase>
    <goals>
        <goal>check</goal>
    </goals>
    <configuration>  
5. Le seuil de couverture
<!-- desactive localement: -Djacoco.check.skip=true -->
                <skip>${jacoco.check.skip}</skip>
                <rules>
                    <rule>
                        <element>BUNDLE</element>
                        <limits>
                            <limit>
                                <counter>LINE</counter>
                                <value>COVEREDRATIO</value>
                                <minimum>${jacoco.minimum.coverage}</minimum>
                            </limit>
                        </limits>
                    </rule>
                </rules>
            </configuration>
        </execution>
    </executions>
</plugin>

<!-- Sonar -->
<plugin>
    <groupId>org.sonarsource.scanner.maven</groupId>
    <artifactId>sonar-maven-plugin</artifactId>
    <version>4.0.0.4121</version>
</plugin>

Ajout de profils (Optionnel)
<profiles>
  <profile>
      <id>coverage-staging</id>
      <properties>
          <jacoco.minimum.coverage>0.70</jacoco.minimum.coverage>
      </properties>
  </profile>

  <profile>
      <id>coverage-main</id>
      <properties>
          <jacoco.minimum.coverage>0.80</jacoco.minimum.coverage>
      </properties>
  </profile>
</profiles>

Ces profils illustrent une approche avancée permettant de simuler localement le comportement des pipelines staging et main


Utilisation:
En local pour tester la couverture, le profil agit comme un raccourci:
./mvnw clean verify -Pcoverage-staging
./mvnw clean verify -Pcoverage-main
L'autre option est utiliser les mêmes commandes qu'en CI:
./mvnw clean verify -Djacoco.minimum.coverage=0.70
./mvnw clean verify -Djacoco.minimum.coverage=0.80 

Pas de fichier "sonar-project.properties"
Fichier de configuration principal utilisé par le SonarScanner CLI pour définir les paramètres d'analyse d'un projet.
Situé à la racine du projet, ce fichier est requis par SonarQube pour tout les projets "non-maven", ce qui n'est pas mon cas.
Pour la connexion SonarQube Cloud: il faudra définir un token, le stocker dans les secrets Github Actions et faire appel à lui dans les pipelines

Pipelines multi-branches avec GitHub Actions

Même code sur toutes les branches

Règles différentes
Les branches [develop] [staging]
Pas de Sonar (Plan Free ne scan que main)
La branche [main]
Sonar activé

La couverture de test doit être mesurée partout, mais imposée uniquement aux points de contrôle du cycle de vie applicatif


Dans la CI:

Le contrôle de couverture intervient en phase verify, après l'exécution des tests et la génération du package

Ordre d'exécution CI:
Spotless → Checkstyle → SpotBugs → Tests unitaires → JaCoCo check → Packaging
Les tests et JaCoCo tournent en premier;
Si la couverture est suffisante (≥ 80%), SonarCloud est exécuté avec le rapport provenant de JaCoCo (XML)
La "Qualité Gate" de Sonar bloque le pipeline en cas de régression sur le “nouveau code”

Runs des workflows par branches
[develop] - fichier ./github/workflows/ci-develop.yaml (extrait)
- name: Run Maven clean verify
    run: ./mvnw -B clean verify jacoco:report

  - name: Upload JaCoCo report
    uses: actions/upload-artifact@v6
    with:
      name: jacoco-report
      path: target/site/jacoco/
  

La commande clean verify lance tous les tests

En dev le seuil n'est pas fixé, c'est à dire qu'il faut le tester avec les commandes CI ou les profils

[staging] - fichier: ./github/workflows/ci-staging.yaml (extrait)
- name: Verify Coverage 70%
  run: ./mvnw -B clean verify jacoco:report -Djacoco.minimum.coverage=0.70

- name: Upload JaCoCo artifacts
  uses: actions/upload-artifact@v6
  with:
    name: jacoco-report
    path: |
      target/site/jacoco/index.html
      target/site/jacoco/jacoco.xml

Simuler la CI pour [staging] avec:
./mvnw clean verify 
  -Djacoco.minimum.coverage=0.70

ou, si vous avez paramétré le profil, utilisez:
./mvnw clean verify -Pcoverage-staging
[main] - fichier: ./github/workflows/cd-prod.yaml (extrait)
- name: Build + Tests + Coverage gate (release)
  if: env.IS_RELEASE == 'true'
  run: ./mvnw -B clean verify -Djacoco.minimum.coverage=0.80

- name: Build + Unit tests only (non-release)
  if: env.IS_RELEASE != 'true'
  run: ./mvnw -B clean test jacoco:report

- name: Upload JaCoCo XML
  uses: actions/upload-artifact@v6
  with:
    name: jacoco-xml
    path: target/site/jacoco/jacoco.xml

- name: SonarCloud analysis
  if: github.ref == 'refs/heads/main'
  env:
    GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
  run: >
    ./mvnw sonar:sonar
    -Dsonar.host.url=https://sonarcloud.io
    -Dsonar.organization=val7304
    -Dsonar.projectKey=${{ secrets.SONAR_PROJECT_KEY }}
    -Dsonar.qualitygate.wait=true


Pareil qu'en staging:

Simuler la CI pour [main] avec:
./mvnw clean verify 
  -Djacoco.minimum.coverage=0.80
Ou, si vous avez paramétré le profil, utilisez:
./mvnw clean verify -Pcoverage-main

Le rapport est toujours généré avec les tests

Avec l'argument -Djacoco.minimum.coverage=0.80, on bloque le pipeline si la couverture est insuffisante

Si les tests passent et que le seuil est respecté, le rapport JaCoCo est produit et uploadé, ensuite SonarCloud s'en sert pour démarrer ses analyses


Utiliser SonarCloud (Free Plan) avec JaCoCo

Avec le plan gratuit SonarCloud, on ne peux analyser qu'une seule branche “complète”: la branche principale.
Les autres branches (develop, staging, feature/…) sont visibles via les analyses de Pull Requests vers cette branche

Pour mettre en place JaCoCo, avec le plan gratuit de SonarCloud sur trois branches ([main] [develop] [staging]), il faut adapter la configuration CI pour contourner l'absence de tableaux de bord dédiés aux branches secondaires

Sur [main]: Chaque scan met à jour la base de référence (couverture, bugs, vulnérabilités, dette) du projet
Sur [develop] & [staging]: On analyse via des PR (vers main) et on voit la couverture et la qualité sur le "nouveau code" de ces branches


Configuration CI/CD
L'auto-analysis SonarCloud ne permet pas d'importer un rapport JaCoCo XML généré par Maven.
Pour un contrôle précis de la couverture, il faut lancer l'analyse depuis la CI, après la génération du rapport.
Voir la documentation officielle sur L'analyse des branches à longue durée de vie
Exemple de logique
Build & Test: On exécute mvnw clean verify pour générer le rapport target/site/jacoco/jacoco.xml grâce au plugin JaCoCo
Scan Sonar: On exécute mvnw sonar:sonar uniquement si on est sur main ou dans le contexte d'une PR
Commande type pour une PR
mvn sonar:sonar \
  -Dsonar.projectKey=YOUR_PROJET \
  -Dsonar.organization=YOUR_ORG \
  -Dsonar.host.url=https://sonarcloud.io \
  -Dsonar.cache.enabled=true \
  -Dsonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml

Relation JaCoCo et SonarCloud (Free Plan)

→ En local: On peut ouvrir le rapport et voir ainsi la couverture détaillée de toutes les classes
→ Sur une PR: SonarCloud nous montre si les nouvelles lignes sont couvertes par des tests
     Si on ajoute 10 lignes, Sonar analyse la couverture du “New Code” définie par la branche cible et compare les métriques au Quality Gate.
→ Sur main: après le merge, une analyse complète met à jour la couverture globale et les métriques de qualité du projet


Limitations importantes du Free Plan
Pas de dashboard pour [develop] / [staging]: On ne peut pas voir l'évolution de la qualité en dehors des rapports de tests locaux ou des logs de la CI
Target branch: L'analyse de PR n'est disponible que si la branche cible est la branche principale définie dans SonarCloud

Visualisation pour Develop/Staging
Puisque le plan gratuit ne crée pas de dashboard pour les autres branches, nous avons deux options:
  • Via PR: Gardez une PR ouverte de develop vers main. Chaque push sur develop mettra à jour l'onglet "Checks" de la PR avec la couverture SonarCloud
  • Rapports locaux/CI: Consultez directement le fichier target/site/jacoco/ généré en local et, dans les artefacts de la CI pour voir la couverture totale de la branche sans passer par SonarCloud

Conclusion
La couverture comme contrat qualité

En pratique, JaCoCo vous donne une mesure factuelle des zones non testées, là où SonarCloud fournit la vue d'ensemble (bugs, vulnérabilités, dette)

  • La CI orchestre ces outils pour transformer la couverture en contrat qualité: Pas de régression - Pas de code non testé qui passe en production
  • Commencez simple (seuil global bas, seuil élevé sur le nouveau code), puis durcissez progressivement les règles sur staging et main au rythme de l'équipe
  • L'objectif n'est pas d'afficher 100% de couverture, mais de livrer du code fiable, soutenu par des tests pertinents

Dans l'article suivant, nous verrons comment poser les bases (branche unique) pour un premier pipeline (main)


Laissez-moi un commentaire

En postant un commentaire anonyme, vous adhérez automatiquement aux conditions d'utilisation du site.