165 votes

Comment configurer Maven + Sonar + JaCoCo pour obtenir un rapport de couverture fusionné ?

Je l'ai cherché sur Internet. Il y a beaucoup de demi-réponses, en rapport avec les propriétés de Maven telles que ${sonar.jacoco.reportPath} ou org.jacoco:jacoco-maven-plugin:prepare-agent ou le réglage maven-surefire-plugin argLine con -javaagent .

D'une manière ou d'une autre, aucune de ces réponses, qu'elles soient isolées ou combinées, ne produit ce que je recherche : Un rapport de couverture qui montre qu'une classe est couverte si elle est utilisée dans des tests plus haut dans la pile, comme les entités utilisées par les DAO, même si elle n'a pas été entièrement couverte par les tests dans son propre module.

Existe-t-il quelque part une configuration définitive pour y parvenir ?

195voto

mpontes Points 772

J'étais dans la même situation que vous, les demi-réponses disséminées sur Internet étaient assez ennuyeuses, car il semblait que de nombreuses personnes avaient le même problème, mais que personne ne pouvait se donner la peine d'expliquer en détail comment elles l'avaient résolu.

Le Docs Sonar se référer à un projet GitHub avec des exemples qui sont utiles. Ce que j'ai fait pour résoudre ce problème a été d'appliquer la logique des tests d'intégration aux tests unitaires ordinaires (bien que des tests unitaires appropriés devraient être spécifiques à un sous-module, ce n'est pas toujours le cas).

Dans le pom.xml parent, ajoutez ces propriétés :

<properties>
    <!-- Sonar -->
    <sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
    <sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
    <sonar.jacoco.reportPath>${project.basedir}/../target/jacoco.exec</sonar.jacoco.reportPath>
    <sonar.language>java</sonar.language>
</properties>

Cela permet à Sonar de récupérer les rapports de tests unitaires pour tous les sous-modules au même endroit (un dossier cible dans le projet parent). Cela indique également à Sonar de réutiliser les rapports exécutés manuellement au lieu de lancer les siens. Nous avons juste besoin de faire fonctionner jacoco-maven-plugin pour tous les sous-modules en le plaçant dans le pom parent, à l'intérieur de build/plugins :

<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.6.0.201210061924</version>
    <configuration>
        <destFile>${sonar.jacoco.reportPath}</destFile>
        <append>true</append>
    </configuration>
    <executions>
        <execution>
            <id>agent</id>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
        </execution>
    </executions>
</plugin>

destFile place le fichier de rapport à l'endroit où Sonar le recherchera et append l'ajoute au fichier au lieu de l'écraser. Ceci combinera tous les rapports JaCoCo pour tous les sous-modules dans le même fichier.

Sonar examinera ce fichier pour chaque sous-module, puisque c'est ce que nous lui avons indiqué ci-dessus, ce qui nous donnera des résultats de tests unitaires combinés pour les fichiers multi-modules dans Sonar.

0 votes

Merveilleux ! Cela a fonctionné. Enfin ! Je pense que l'incantation vitale magique qui me manquait était <append>true</append>.

0 votes

Ça marche ! J'ai dû faire un nouveau mvn package avant l'exécution mvn sonar:sonar pour que le nouveau chemin d'accès au rapport soit généré.

0 votes

Observation supplémentaire pour un projet multi-module, Un projet multi-module est défini par un POM parent référençant un ou plusieurs sous-modules. Ma configuration de construction de reactor était un peu différente. Les projets de sous-modules avaient différents pom 'parent' et pas le projet parent avec pom packaging. Dans ce cas, j'ai dû définir le plugin jacoco dans chaque sous-module. La propriété <sonar.jacoco.reportPath> doit également être remplacée pour obtenir l'agrégation de rapports jacoco souhaitée. Le journal Maven est très pratique. Gardez un œil sur la phase 'jacoco-maven-plugin:xxx:prepare-agent' et le journal 'Sensor JaCoCoSensor'.

33voto

Lonzak Points 1408

NOUVELLE MÉTHODE DEPUIS LA VERSION 0.7.7

Depuis la version 0.7.7, il existe une nouvelle façon de créer un rapport agrégé :

Vous créez un projet "rapport" distinct qui recueille tous les rapports nécessaires (tout objectif du projet agrégateur est exécuté). avant ses modules, il ne peut donc pas être utilisé).

aggregator pom
  |- parent pom
  |- module a
  |- module b
  |- report module 

Le Root pom ressemble à ceci (n'oubliez pas d'ajouter le nouveau module de rapport sous modules) :

<build>
<plugins>
  <plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.7.8</version>
    <executions>
      <execution>
        <id>prepare-agent</id>
        <goals>
          <goal>prepare-agent</goal>
        </goals>
      </execution>
    </executions>
  </plugin>
</plugins>

Les poms de chaque sous-module n'ont pas besoin d'être modifiés. Le pom du module module de rapport ressemble à ceci :

<!-- Add all sub modules as dependencies here -->
<dependencies>
  <dependency>
    <module a>
  </dependency>
  <dependency>
    <module b>
  </dependency>
 ...

  <build>
    <plugins>
      <plugin>
        <groupId>org.jacoco</groupId>
        <artifactId>jacoco-maven-plugin</artifactId>
        <version>0.7.8</version>
        <executions>
          <execution>
            <id>report-aggregate</id>
            <phase>verify</phase>
            <goals>
              <goal>report-aggregate</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

Un exemple complet peut être trouvé aquí .

26voto

ZuzEL Points 1003

FAQ

Les questions qui me viennent à l'esprit depuis cette époque sont devenues folles avec jacoco.

Mon serveur d'application (jBoss, Glassfish..) est situé en Irak, en Syrie, etc. Est-il possible d'obtenir une couverture multi-modules en exécutant des tests d'intégration sur ce serveur ? Jenkins et Sonar sont également sur des serveurs différents.

Oui, vous devez utiliser agent jacoco qui fonctionne en mode output=tcpserver , jacoco ant lib. En gros, deux jar s. Vous obtiendrez ainsi un taux de réussite de 99 %.

Comment fonctionne l'agent jacoco ?

Vous ajoutez une chaîne de caractères

-javaagent:[your_path]/jacocoagent.jar=destfile=/jacoco.exec,output=tcpserver,address=*

à votre serveur d'application JAVA_OPTS et redémarrez-le. Dans cette chaîne, seul [your_path] doit être remplacé par le chemin vers jacocoagent.jar, stocké (stockez-le !) sur votre VM où tourne le serveur d'application. A partir du moment où vous démarrez le serveur d'application, toutes les applications déployées seront surveillées dynamiquement et leur activité (c'est à dire l'utilisation du code) sera prête à être récupérée au format jacocos .exec par une requête tcl.

Puis-je réinitialiser l'agent jacoco pour qu'il ne collecte les données d'exécution qu'à partir du moment où mon test a commencé ?

Oui, pour cela vous avez besoin de jacocoant.jar et ant build script situés dans votre espace de travail jenkins.

En fait, ce dont j'ai besoin à http://www.eclemma.org/jacoco/, c'est de jacocoant.jar situé dans mon workspace jenkins, et de jacocoagent.jar situé sur mon serveur d'applications VM ?

C'est exact.

Je ne veux pas utiliser ant, j'ai entendu dire que le plugin jacoco maven peut faire tout cela aussi.

Ce n'est pas exact, le plugin jacoco maven peut collecter les données des tests unitaires et certaines données des tests d'intégration (voir Arquillian Jacoco ), mais si vous avez par exemple des tests assurés par rest comme un build séparé dans jenkins, et que vous voulez montrer la couverture multi-module, je ne vois pas comment le plugin maven peut vous aider.

Que produit exactement l'agent jacoco ?

Seules les données de couverture en .exec format. Sonar peut alors le lire.

Est-ce que jacoco a besoin de savoir où se trouvent mes classes java ?

Non, le sonar le fait, mais pas le jacoco. Lorsque vous faites mvn sonar:sonar Le chemin vers les classes entre en jeu.

Qu'en est-il de la fourmi script ?

Il doit être présenté dans votre espace de travail jenkins. Le mien est script, je l'ai appelé jacoco.xml ressemble à cela :

<project name="Jacoco library to collect code coverage remotely" xmlns:jacoco="antlib:org.jacoco.ant">
    <property name="jacoco.port" value="6300"/>
    <property name="jacocoReportFile" location="${workspace}/it-jacoco.exec"/>

    <taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml">
        <classpath path="${workspace}/tools/jacoco/jacocoant.jar"/>
    </taskdef>

    <target name="jacocoReport">
            <jacoco:dump address="${jacoco.host}" port="${jacoco.port}" dump="true" reset="true" destfile="${jacocoReportFile}" append="false"/>
    </target>

    <target name="jacocoReset">
            <jacoco:dump address="${jacoco.host}" port="${jacoco.port}" reset="true" destfile="${jacocoReportFile}" append="false"/>
        <delete file="${jacocoReportFile}"/>
    </target>
</project>

Deux paramètres obligatoires que vous devez passer lorsque vous invoquez ce script. -Dworkspace=$WORKSPACE utilisez-le pour pointer vers votre espace de travail jenkins et -Djacoco.host=yourappserver.com hôte sans http://

Remarquez également que j'ai mis mon jacocoant.jar vers ${workspace}/tools/jacoco/jacocoant.jar

Que dois-je faire ensuite ?

Avez-vous démarré votre serveur d'application avec jacocoagent.jar ?

Avez-vous mis ant script et jacocoant.jar dans votre workspace jenkins ?

Si oui, la dernière étape est de configurer un build jenkins. Voici la stratégie :

  1. Invoquer la cible de la fourmi jacocoReset pour réinitialiser toutes les données collectées précédemment.
  2. Exécutez vos tests
  3. Invoquer la cible de la fourmi jacocoReport pour obtenir un rapport

Si tout va bien, vous verrez it-jacoco.exec dans votre espace de travail.

Regardez la capture d'écran, j'ai aussi ant installé dans mon espace de travail en $WORKSPACE/tools/ant mais vous pouvez en utiliser un qui est installé dans votre Jenkins.

enter image description here

Comment pousser ce rapport dans le sonar ?

Maven sonar:sonar fera le travail (n'oubliez pas de le configurer), pointez-le vers le pom.xml principal pour qu'il s'exécute à travers tous les modules. Utiliser sonar.jacoco.itReportPath=$WORKSPACE/it-jacoco.exec pour indiquer au sonar où se trouve votre rapport de test d'intégration. Chaque fois qu'il analysera de nouvelles classes de modules, il recherchera des informations sur la couverture dans le fichier it-jacoco.exec .

J'ai déjà jacoco.exec dans mon répertoire `target`, `mvn sonar:sonar` l'ignore/le supprime.

Par défaut mvn sonar:sonar fait clean et supprime votre répertoire cible, utilisez sonar.dynamicAnalysis=reuseReports pour l'éviter.

20voto

keddok Points 59

J'ai trouvé une autre solution pour les nouvelles versions de Sonar où le format de rapport binaire de JaCoCo (*.exec) a été déprécié et le format préféré est XML (SonarJava 5.12 et plus). La solution est très simple et similaire à la solution précédente avec les rapports *.exec dans le répertoire parent de ce sujet : https://stackoverflow.com/a/15535970/4448263 .

En supposant que la structure de notre projet soit la suivante

moduleC - aggregate project's pom
  |- moduleA - some classes without tests
  |- moduleB - some classes depending from moduleA and tests for classes in both modules: moduleA and moduleB

Vous avez besoin de la configuration suivante du plugin de construction maven dans le pom du projet agrégé :

<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.5</version>
    <executions>
        <execution>
            <id>prepare-and-report</id>
            <goals>
                <goal>prepare-agent</goal>
                <goal>report</goal>
            </goals>
        </execution>
        <execution>
            <id>report-aggregate</id>
            <phase>verify</phase>
            <goals>
                <goal>report-aggregate</goal>
            </goals>
            <configuration>
                <outputDirectory>${project.basedir}/../target/site/jacoco-aggregate</outputDirectory>
            </configuration>
        </execution>
    </executions>
</plugin>

Puis construire le projet avec maven :

mvn clean verify

Pour Sonar, vous devez définir les propriétés dans l'interface graphique d'administration :

sonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml,../target/site/jacoco-aggregate/jacoco.xml

ou en utilisant la ligne de commande :

mvn sonar:sonar -Dsonar.coverage.jacoco.xmlReportPaths=target/site/jacoco/jacoco.xml,../target/site/jacoco-aggregate/jacoco.xml

Description

Cela crée des rapports binaires pour chaque module dans les répertoires par défaut : target/jacoco.exec . Il crée ensuite des rapports XML pour chaque module dans les répertoires par défaut : target/site/jacoco/jacoco.xml . Il crée ensuite un rapport agrégé pour chaque module dans le répertoire personnalisé. ${project.basedir}/../target/site/jacoco-aggregate/ qui est relative au répertoire parent de chaque module. Pour le moduleA et le moduleB, il s'agira du chemin commun moduleC/target/site/jacoco-aggregate/ .

Comme le module B dépend du module A, le module B sera construit en dernier et son rapport sera utilisé comme rapport de couverture globale dans Sonar pour les modules A et B.

En plus du rapport agrégé, nous avons besoin d'un rapport de module normal car les rapports agrégés de JaCoCo ne contiennent des données de couverture que pour les dépendances.

Ensemble, ces deux types de rapports fournissent des données de couverture complètes pour Sonar.

Il y a une petite restriction : vous devez pouvoir écrire un rapport dans le répertoire parent du projet (vous devez avoir la permission). Vous pouvez également définir la propriété jacoco.skip=true dans le pom.xml du projet Root (moduleC) et jacoco.skip=false dans des modules avec des classes et des tests (moduleA et moduleB).

12voto

markdsievers Points 1845

Je vais publier ma solution, car elle est subtilement différente des autres et il m'a fallu une bonne journée pour y parvenir, avec l'aide des réponses existantes.

Pour un projet Maven multi-modules :

ROOT
|--WAR
|--LIB-1
|--LIB-2
|--TEST

Où le WAR est l'application web principale, LIB 1 et 2 sont des modules supplémentaires. WAR dépend de et TEST est l'endroit où se trouvent les tests d'intégration. TEST lance une instance Tomcat intégrée (pas via le plugin Tomcat) et exécute WAR et les teste à l'aide d'un ensemble de tests JUnit. Les tests WAR y LIB ont tous deux leurs propres tests unitaires.

Le résultat de tout cela est que la couverture des tests d'intégration et des tests unitaires est séparée et peut être distinguée dans SonarQube.

ROOT pom.xml

<!-- Sonar properties-->
<sonar.jacoco.itReportPath>${project.basedir}/../target/jacoco-it.exec</sonar.jacoco.itReportPath>
<sonar.jacoco.reportPath>${project.basedir}/../target/jacoco.exec</sonar.jacoco.reportPath>
<sonar.language>java</sonar.language>
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>

<!-- build/plugins (not build/pluginManagement/plugins!) -->
<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.7.6.201602180812</version>
    <executions>
        <execution>
            <id>agent-for-ut</id>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
            <configuration>
                <append>true</append>
                <destFile>${sonar.jacoco.reportPath}</destFile>
            </configuration>
        </execution>
        <execution>
            <id>agent-for-it</id>
            <goals>
                <goal>prepare-agent-integration</goal>
            </goals>
            <configuration>
                <append>true</append>
                <destFile>${sonar.jacoco.itReportPath}</destFile>
            </configuration>
        </execution>
    </executions>
</plugin>

WAR , LIB y TEST pom.xml héritera de l'exécution des plugins JaCoCo.

TEST pom.xml

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-failsafe-plugin</artifactId>
    <version>2.19.1</version>
    <executions>
        <execution>
            <goals>
                <goal>integration-test</goal>
                <goal>verify</goal>
            </goals>
            <configuration>
                <skipTests>${skip.tests}</skipTests>
                <argLine>${argLine} -Duser.timezone=UTC -Xms256m -Xmx256m</argLine>
                <includes>
                    <includes>**/*Test*</includes>
                </includes>
            </configuration>
        </execution>
    </executions>
</plugin>

J'ai également trouvé Petri Kainulainens blog post 'Creating Code Coverage Reports for Unit and Integration Tests With the JaCoCo Maven Plugin' (Créer des rapports de couverture de code pour les tests unitaires et d'intégration avec le plugin JaCoCo Maven) pour la mise en place de JaCoCo.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X