Couverture de code multi-module avec Jacoco et Sonar

J’ai récemment été confronté à un problème à priori courant, mais sur lequel j’ai trouvé peu de documentation. Je vous partage donc le résultat de mes expérimentations, en espérant vous faire économiser quelques heures de recherches.

Le code source est présent ici. Chaque commit montre une étape de résolution du problème

Il faut noter que cet article ne parle pas de la pertinence ou non de faire du coverage, mais simplement de s’assurer que, si cette métrique est utilisée, elle le soit correctement.

Le problème

Je travaille sur un projet Maven composé de plusieurs modules (ici uniquement 2 pour simplifier), et certains dépendent des autres. Voilà une représentation simpliste du projet :

-module A
  - pom.xml
- module B (dépend de A)
  - pom.xml
- pom.xml

État initial du projet

J’ai principalement 2 types des tests :

  • des TU dans le module A
  • des tests “de bout en bout” dans le module B

La situation est la suivante :

  • les tests du module B passent sur du code du module A (car B dépend de A)
  • les TU ne couvrent pas tout le code du module A (car les tests de B passent déjà dessus).

Par défaut, Jacoco ne sait pas calculer une couverture de tests sur un module externe. En d’autres termes, les tests exécutés sur B ne prendront pas en compte la couverture effectuée sur A (je n’ai pas creusé le pourquoi en détail, mais je suppose que le coverage est écrasé entre chaque test de module).

Une fois Jacoco configuré (voir ici), on remarque que le coverage n’est pas de 100%.

Screenshot-2019-01-02-at-17.05.16

En détails :

Screenshot-2019-01-02-at-17.06.24

Encore plus en détails :

Screenshot-2019-01-02-at-17.07.12

La solution

La solution à ce problème est assez simple, et tient en 2 parties à effectuer dans le pom.xml racine :

  • configurer le plugin Jacoco pour merger le coverage de chaque module dans un fichier global (à la racine du module par exemple),
  • indiquer à Sonar d’utiliser ce fichier compilé plutôt que les fichiers de modules.
     <build>
         <plugins>
             <plugin>
                 <groupId>org.jacoco</groupId>
                 <artifactId>jacoco-maven-plugin</artifactId>
                 <version>0.8.2</version>
                 <executions>
                 [...]
                    <execution>
	                        <id>merge</id>
	                        <goals>
	                            <goal>merge</goal>
	                        </goals>
	                        <configuration>
	                            <fileSets>
	                                <fileSet implementation="org.apache.maven.shared.model.fileset.FileSet">
	                                    <directory>${project.basedir}</directory>
	                                    <includes>
	                                        <include>**/*.exec</include>
	                                    </includes>
	                                </fileSet>
	                            </fileSets>
	                        </configuration>
	                    </execution>
                 [...]
  <properties>
	[...]
	<!-- Tell sonar where to look for the coverage file. Property inherited by submodules -->  
     					 		<sonar.jacoco.reportPath>${project.basedir}/../target/jacoco.exec</sonar.jacoco.reportPath>
	</properties>

Ces modifications sont visibles ici.

Une fois effectuées, voilà le résultat dans Sonar :

Screenshot-2019-01-02-at-17.11.24

Conclusion

Le problème est résolu en quelques lignes de configuration… mais encore faut-il le savoir.

Pour aller plus loin, Jacoco est capable de fournir un rapport de coverage “hors de Sonar”, mais je n’ai pas réussi à le faire fonctionner correctement. J’accepte les pull-requests si vous en avez le courage, sinon je ferai un nouvel article le jour où j’en aurai besoin !