How to set up a multi-module Maven + Sonar + JaCoCo to provide a consolidated coverage report?

I searched up and down the internet for this. There are many semi answers to use Maven properties like ${sonar.jacoco.reportPath} or org.jacoco:jacoco-maven-plugin:prepare-agent , or install maven-surefire-plugin argLine using -javaagent .

Some of them, none of these answers, either alone or in combination, produce what I need: A coverage report that shows the class that is considered if used in tests above the stack, such as entities used by the DAO although it was not fully covered by tests in its own module.

Is there any specific configuration to achieve this, please?

+108
maven code-coverage sonarqube jacoco
Oct 23 '12 at 13:11
source share
8 answers

I was in the same situation as you, half of the answers scattered all over the Internet were rather annoying, because it seemed like many people had the same problem, but no one could bother to fully explain how they solved it have decided.

Sonar documents are related to the GitHub project with examples that are useful. What I did to solve this problem was to apply the integration test logic for regular unit tests (although the right unit tests should be specific to the submodule, this is not always the case).

In the parent pom.xml add the following properties:

 <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> 

This will force Sonar to collect unit test reports for all submodules in the same place (destination folder in the parent project). He also reports that Sonar is reusing manual reports instead of collapsing its own. We just need to make a jacoco-maven-plugin for all submodules by putting this in the parent pom, inside 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 places the report file where Sonar will look for it, and append adds it to the file, rather than overwriting it. This will merge all JaCoCo reports for all submodules in the same file.

Sonar will look at this file for each submodule, as we indicated to it above, giving us the results of combined testing for files with several modules in Sonar.

+150
Mar 20 '13 at 22:24
source share

reference

Questions from the top of my head since that time I went crazy with jacoco.

My application server (jBoss, Glassfish ..), located in Iraq, Syria, whatever it is. Is it possible to get multi-module coverage when performing integration tests? Jenkins and Sonar are also located on different servers.

Yes. You should use jacoco agent , which works in output=tcpserver mode, jacoco ant lib. Basically two jar s. This will give you 99% success.

How does jacoco agent work?

You add line

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

to your JAVA_OPTS application server and reload it. In this line, only [your_path] needs to be replaced with the path to jacocoagent.jar, save (save it!) On your virtual machine where the application server is running. From this moment you start the application server, all applications that will be deployed will be dynamically controlled, and their activity (the meaning of using the code) will be ready for you in jacocos.exec format at the request of tcl.

Can I reset the jacoco agent to start collecting execution data only from the moment the test starts?

Yes, for this purpose you need jacocoant.jar and ant build script located in the jenkins workspace.

So basically what I need from http://www.eclemma.org/jacoco/ is jacocoant.jar located in my jenkins workspace and jacocoagent.jar located on my VM application server?

It is right.

I do not want to use ant, I heard that the jacoco maven plugin can do everything too.

Wrong, the jacoco maven plugin can collect unit test data and some integration test data (see Arquillian Jacoco ), but if you have, for example, saving completed tests as split assemblies in jenkins and want to show multi-module coverage, I don’t see how it can help maven plugin.

What exactly does the jacoco agent produce?

Only coverage data in .exec format. Then the sonar can read it.

Do jacoco need to know where my java classes are?

No, sonar does, but not jacoco. When you do mvn sonar:sonar , the class path comes into play.

So what about ant script?

It should be presented in the jenkins workspace. Mine ant script, I called it jacoco.xml looks like this:

 <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> 

Two required parameters that you must pass when calling script -Dworkspace=$WORKSPACE use it to point to the jenkins workspace and -Djacoco.host=yourappserver.com host without http://

Also note that I put my jacocoant.jar in $ {workspace} /tools/jacoco/jacocoant.jar

What should I do next?

Have you started your application server using jacocoagent.jar?

Have you put ant script and jacocoant.jar in your jenkins workspace?

If so, the last step is to configure the jenkins build. Here is the strategy:

  • Call ant target jacocoReset to reset all previously collected data.
  • Run tests
  • Call ant target jacocoReport to get a report

If everything is correct, you will see it-jacoco.exec in the assembly workspace.

Look at the screenshot, I also have ant installed in my workspace in the $WORKSPACE/tools/ant directory, but you can use the one installed in your jenkins.

enter image description here

How to move this report to sonar?

Maven sonar:sonar will complete the task (do not forget to configure it), point it to the main pom.xml so that it runs through all modules. Use the sonar.jacoco.itReportPath=$WORKSPACE/it-jacoco.exec to tell the sonar where your integration report is located. Each time he analyzes new classes of modules, he will look for coverage information in it-jacoco.exec .

I already have jacoco.exec in my `target` directory,` mvn sonar: sonar` ignores / removes it

By default, mvn sonar:sonar runs clean and removes your target directory, use sonar.dynamicAnalysis=reuseReports to avoid it.

+23
May 23 '14 at 13:42
source share

NEW WAY FROM VERSION 0.7.7

Starting with version 0.7.7, a new way to create an aggregated report appeared:

You create a separate project β€œreport”, which collects all the necessary reports (any goal in the project of the aggregator is executed before its modules, therefore it cannot be used).

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

The root pom looks like this (do not forget to add a new report module to the 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> 

Pom from each additional module does not need to be changed at all. Pom from the report module is as follows:

 <!-- 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> 

Here you can find the full.

+17
Jan 13 '17 at 15:03
source share

I will lay out my decision, since it is subtly different from others, and also took me a hard day to cope with the existing answers.

For a multi-module Maven project:

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

If the WAR project is the main web application, LIB 1 and 2 are add-on modules on which WAR depends, and TEST are those integration tests. TEST creates an embedded Tomcat instance (not through the Tomcat plugin) and runs the WAR project and validates them using the JUnit test suite. WAR and LIB projects have their own unit tests.

The result of all this is integration and unit test coverage, which is shared and can be highlighted in 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 and TEST pom.xml inherit the execution of JaCoCo plugins.

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> 

I also found Petri Kainulainens' blog post "Reporting code coverage for unit and integration tests with the JaCoCo Maven plugin" to be valuable for JaCoCo sets things up.

+10
Jun 17 '16 at 0:04
source share

The configuration that I use at my parent level, where I have separate stages of testing and integration.

I configure the following properties in the parent POM Properties

  <maven.surefire.report.plugin>2.19.1</maven.surefire.report.plugin> <jacoco.plugin.version>0.7.6.201602180812</jacoco.plugin.version> <jacoco.check.lineRatio>0.52</jacoco.check.lineRatio> <jacoco.check.branchRatio>0.40</jacoco.check.branchRatio> <jacoco.check.complexityMax>15</jacoco.check.complexityMax> <jacoco.skip>false</jacoco.skip> <jacoco.excludePattern/> <jacoco.destfile>${project.basedir}/../target/coverage-reports/jacoco.exec</jacoco.destfile> <sonar.language>java</sonar.language> <sonar.exclusions>**/generated-sources/**/*</sonar.exclusions> <sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin> <sonar.coverage.exclusions>${jacoco.excludePattern}</sonar.coverage.exclusions> <sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis> <sonar.jacoco.reportPath>${project.basedir}/../target/coverage-reports</sonar.jacoco.reportPath> <skip.surefire.tests>${skipTests}</skip.surefire.tests> <skip.failsafe.tests>${skipTests}</skip.failsafe.tests> 

I host the plugin definitions under the control of the plugin.

Note that I am defining a property for the surefire (surefireArgLine) and failafe (failafeArgLine) arguments to allow jacoco to configure javaagent to run with each test.

In the pluginManagement section

  <build> <pluginManagment> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <fork>true</fork> <meminitial>1024m</meminitial> <maxmem>1024m</maxmem> <compilerArgument>-g</compilerArgument> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> <encoding>${project.build.sourceEncoding}</encoding> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.19.1</version> <configuration> <forkCount>4</forkCount> <reuseForks>false</reuseForks> <argLine>-Xmx2048m ${surefireArgLine}</argLine> <includes> <include>**/*Test.java</include> </includes> <excludes> <exclude>**/*IT.java</exclude> </excludes> <skip>${skip.surefire.tests}</skip> </configuration> </plugin> <plugin> <!-- For integration test separation --> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> <version>2.19.1</version> <dependencies> <dependency> <groupId>org.apache.maven.surefire</groupId> <artifactId>surefire-junit47</artifactId> <version>2.19.1</version> </dependency> </dependencies> <configuration> <forkCount>4</forkCount> <reuseForks>false</reuseForks> <argLine>${failsafeArgLine}</argLine> <includes> <include>**/*IT.java</include> </includes> <skip>${skip.failsafe.tests}</skip> </configuration> <executions> <execution> <id>integration-test</id> <goals> <goal>integration-test</goal> </goals> </execution> <execution> <id>verify</id> <goals> <goal>verify</goal> </goals> </execution> </executions> </plugin> <plugin> <!-- Code Coverage --> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>${jacoco.plugin.version}</version> <configuration> <haltOnFailure>true</haltOnFailure> <excludes> <exclude>**/*.mar</exclude> <exclude>${jacoco.excludePattern}</exclude> </excludes> <rules> <rule> <element>BUNDLE</element> <limits> <limit> <counter>LINE</counter> <value>COVEREDRATIO</value> <minimum>${jacoco.check.lineRatio}</minimum> </limit> <limit> <counter>BRANCH</counter> <value>COVEREDRATIO</value> <minimum>${jacoco.check.branchRatio}</minimum> </limit> </limits> </rule> <rule> <element>METHOD</element> <limits> <limit> <counter>COMPLEXITY</counter> <value>TOTALCOUNT</value> <maximum>${jacoco.check.complexityMax}</maximum> </limit> </limits> </rule> </rules> </configuration> <executions> <execution> <id>pre-unit-test</id> <goals> <goal>prepare-agent</goal> </goals> <configuration> <destFile>${jacoco.destfile}</destFile> <append>true</append> <propertyName>surefireArgLine</propertyName> </configuration> </execution> <execution> <id>post-unit-test</id> <phase>test</phase> <goals> <goal>report</goal> </goals> <configuration> <dataFile>${jacoco.destfile}</dataFile> <outputDirectory>${sonar.jacoco.reportPath}</outputDirectory> <skip>${skip.surefire.tests}</skip> </configuration> </execution> <execution> <id>pre-integration-test</id> <phase>pre-integration-test</phase> <goals> <goal>prepare-agent-integration</goal> </goals> <configuration> <destFile>${jacoco.destfile}</destFile> <append>true</append> <propertyName>failsafeArgLine</propertyName> </configuration> </execution> <execution> <id>post-integration-test</id> <phase>post-integration-test</phase> <goals> <goal>report-integration</goal> </goals> <configuration> <dataFile>${jacoco.destfile}</dataFile> <outputDirectory>${sonar.jacoco.reportPath}</outputDirectory> <skip>${skip.failsafe.tests}</skip> </configuration> </execution> <!-- Disabled until such time as code quality stops this tripping <execution> <id>default-check</id> <goals> <goal>check</goal> </goals> <configuration> <dataFile>${jacoco.destfile}</dataFile> </configuration> </execution> --> </executions> </plugin> ... 

And in the assembly section

  <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> </plugin> <plugin> <!-- for unit test execution --> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> </plugin> <plugin> <!-- For integration test separation --> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> </plugin> <plugin> <!-- For code coverage --> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> </plugin> .... 

And in the report section

  <reporting> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-report-plugin</artifactId> <version>${maven.surefire.report.plugin}</version> <configuration> <showSuccess>false</showSuccess> <alwaysGenerateFailsafeReport>true</alwaysGenerateFailsafeReport> <alwaysGenerateSurefireReport>true</alwaysGenerateSurefireReport> <aggregate>true</aggregate> </configuration> </plugin> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>${jacoco.plugin.version}</version> <configuration> <excludes> <exclude>**/*.mar</exclude> <exclude>${jacoco.excludePattern}</exclude> </excludes> </configuration> </plugin> </plugins> </reporting> 
+5
May 03 '16 at 6:42
source share

There is a way to do this. The magic is to create a combined jacoco.exec file. And with maven 3.3.1 there is an easy way to get this. Here is my profile:

 <profile> <id>runSonar</id> <activation> <property> <name>runSonar</name> <value>true</value> </property> </activation> <properties> <sonar.language>java</sonar.language> <sonar.host.url>http://sonar.url</sonar.host.url> <sonar.login>tokenX</sonar.login> <sonar.jacoco.reportMissing.force.zero>true</sonar.jacoco.reportMissing.force.zero> <sonar.jacoco.reportPath>${jacoco.destFile}</sonar.jacoco.reportPath> <jacoco.destFile>${maven.multiModuleProjectDirectory}/target/jacoco_analysis/jacoco.exec</jacoco.destFile> </properties> <build> <plugins> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <executions> <execution> <id>default-prepare-agent</id> <goals> <goal>prepare-agent</goal> </goals> <configuration> <append>true</append> <destFile>${jacoco.destFile}</destFile> </configuration> </execution> </executions> </plugin> </plugins> <pluginManagement> <plugins> <plugin> <groupId>org.sonarsource.scanner.maven</groupId> <artifactId>sonar-maven-plugin</artifactId> <version>3.2</version> </plugin> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.7.8</version> </plugin> </plugins> </pluginManagement> </build> </profile> 

If you add this profile to the parent pom and call mvn clean install sonar:sonar -DrunSonar , you get full coverage.

The magic here is maven.multiModuleProjectDirectory . This folder is always the folder in which you started the maven build.

+5
Feb 02 '17 at 16:33
source share

You can call the ant task named merge on maven to put all the coverage files (*. Exec) together in the same file.

If you run unit tests, use the prepare-package phase, if you run the integration test, use the post-integration test .

This site shows an example of calling a jacoco ant task in a maven project

You can use this combined file on a sonar.

+1
Oct 23 '12 at 19:39
source share

for unit testing and integration testing, you can use the maven-surefire-plugin and the maven-fail-safe plugin with limited inclusion / exclusion. I played with CDI when I touched sonar / jacoco, so I ended up in this project:

https://github.com/FibreFoX/cdi-sessionscoped-login/

Maybe this will help you a bit. in my pom.xml I use "-javaagent" implicitly by setting the argLine parameter in the configuration section of the specified test plugins. The explicit use of ANT in MAVEN projects is something that I would not try, for me it mixed a lot of two worlds.

I only have a single-mode maven project, but maybe it will help you customize your work.

Note: maybe not all maven plugins are up2date, maybe some problems are fixed in later versions

0
Feb 20 '13 at 12:51
source share



All Articles