Sunday, December 10, 2017

Code Coverage - JaCoCo with Maven multi-module project

In this post, we will see how to configure the JaCoCo for Maven multi-module projects.
Previously, JaCoCo did not support the multi modules maven project, but with the latest version, it adds in the supports. JaCoCo has provided its sample at Git under its maven plugin test at it-report-aggregate. Let's use this sample project to see how it works.

First download the code by using command
svn checkout https://github.com/jacoco/jacoco/trunk/jacoco-maven-plugin.test/it/it-report-aggregate

This project is a parent-child project. It contains four modules, the child1, child1-test, child2 and report. The report project is used to create a centralized code coverage report. 


This project is a child project in Jacoco project by default, it has configured to have "setup-parent" as its parent project in the pom file. To make this project works as an independent project, we need to do a little modifications.
First, remove the parent configuration at it-report-aggregate/pom.xml and add in the groupId and version as below (remove the parent and add in the lines flagged as blue)


 <parent>

    <groupId>jacoco</groupId>
    <artifactId>setup-parent</artifactId>
    <version>1.0-SNAPSHOT</version>
  </parent>

  <groupId>jacoco</groupId>
  <artifactId>it-report-aggregate</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>pom</packaging>

Second, copy in all the dependences and plugin configurations from Jacobo/jacoco-maven-plugin.test/it/setup-parent/pom.xml to this pom.xml.

Third, change the "@project.groupId@" to "org.jacoco" for plugin configuration in this pom.xml and also the one under folder it-report-aggregate and it-report-aggregate/report.

Run the command "mvn clean verify", the report will be generated under report/target/site/jacoco-aggregate. However, it only includes the report for child1 and child1-test. Check at the file report/pom.xml. you will see that the module child1 and child2 are configured at different scope as dependency.

  <dependencies>
    <dependency>
      <groupId>jacoco</groupId>
      <artifactId>child1</artifactId>
      <version>${project.version}</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>jacoco</groupId>
      <artifactId>child1-test</artifactId>
      <version>${project.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>jacoco</groupId>
      <artifactId>child2</artifactId>
      <version>${project.version}</version>
      <scope>runtime</scope>
    </dependency>
  </dependencies>

Change the scope of child2 to compile, and run command "mvn clean verify" again, you will see the report generated with all three modules then.It automatically generates the report for all the modules.


Let's come back to see what configurations it has done. 
Check on all the pom.xml in the projects, we will find only the pom files under it-report-aggregate and it-report-aggregate/report have been added in the configurations for JaCoCo. 
  • it-report-aggregate/pom.xml:  It defined the JaCoCo plugin at "prepare-agent". All the child modules will inherit this configurations, so that's why you will see the Jacobo.exec generated under all child module target folder.
  <build>
    <plugins>
      <plugin>
        <groupId>@project.groupId@</groupId>
        <artifactId>jacoco-maven-plugin</artifactId>
        <executions>
          <execution>
            <id>prepare-agent</id>
            <goals>
              <goal>prepare-agent</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  • it-report-aggregation/report/pom.xml: it defined all other three modules as dependencies and also add in the JaCoCo plugin at "report-aggregate". This is the new feature that JaCoCo just added in for supporting this multi module case at version 0.7.7.

  <dependencies>
    <dependency>
      <groupId>jacoco</groupId>
      <artifactId>child1</artifactId>
      <version>${project.version}</version>
      <scope>compile</scope>
    </dependency>
    <dependency>
      <groupId>jacoco</groupId>
      <artifactId>child1-test</artifactId>
      <version>${project.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>jacoco</groupId>
      <artifactId>child2</artifactId>
      <version>${project.version}</version>
      <scope>runtime</scope>
    </dependency>
  </dependencies>

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

This is very clear on how to configure the JaCoCo, below is the summary steps.
1. add in the JaCoCo plugin for prepare-agent at parent pom
2. create a separate module in the project and announce it as a child from the parent
3. add all modules as dependences into this new module
4. add in the JaCoCo plugin for report-aggregate at the new module's pom

Refer the official Wiki for more information.

Work-Arounds
The life is much better if we could have a backup plan B. The JaCoCo Ant task has provide the alter way to create the centralized report for Maven multi module project. To use the ant tasks in maven plugin, we will need to use the plugin "maven-antrun-plugin".  Let's continue to use the same sample project to see how could we make it.

Create a new module named "report2" to demo this method. In the pom.xml, announce the same parent and dependences as the report module. Then we add in the maven-dependcy-plugin to copy the jacocoant.jar file to local. 

<plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-dependency-plugin</artifactId>
   <executions>
<!-- Copy the ant tasks jar. Needed for ts.jacoco.report-ant . -->
<execution>
        <id>jacoco-dependency-ant</id>
        <goals>
<goal>copy</goal>
        </goals>
        <phase>process-test-resources</phase>
        <inherited>false</inherited>
        <configuration>
<artifactItems>
             <artifactItem>
                <groupId>org.jacoco</groupId>
                <artifactId>org.jacoco.ant</artifactId>
                 <version>0.7.6.201602180812</version>
             </artifactItem>
</artifactItems>
<stripVersion>true</stripVersion>
<outputDirectory>${basedir}/target/jacoco-jars</outputDirectory>
        </configuration>
</execution>
    </executions>
</plugin>

After this configure the ant task, the ant task will use the existing jacocoant.jar and computing the coverage. The configuration is very similar with JaCoCo Ant Task.

<plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-antrun-plugin</artifactId>
   <version>1.6</version><!--$NO-MVN-MAN-VER$ -->
   <inherited>false</inherited>
   <executions>
      <execution>
<phase>post-integration-test</phase>
<goals>
           <goal>run</goal>
</goals>
<configuration>
           <target>
              <echo message="Generating JaCoCo Reports" />
              <taskdef name="report" classname="org.jacoco.ant.ReportTask">
                <classpath path="../target/jacoco-jars/org.jacoco.ant.jar" />
              </taskdef>
              <report>
                <executiondata>
                   <fileset dir="../child1/target">
                       <include name="jacoco.exec" />
                   </fileset>
                   <fileset dir="../child1-test/target">
                      <include name="jacoco.exec" />
                    </fileset>
                   <fileset dir="../child2/target">
                      <include name="jacoco.exec" />
                   </fileset>
                 </executiondata>
                <structure name="rmdaalser Coverage Project">
                   <group name="rmdaalser">
                      <sourcefiles encoding="UTF-8">
                           <fileset dir="./child1/src/main/java" />
                        <fileset dir="../child2/src/main/java" />
                      </sourcefiles>
                      <classfiles>
                        <fileset dir="../child1/target/classes" />
                        <fileset dir="../child2/target/classes" />
                      </classfiles>
                    </group>
                </structure>
                 <html destdir="${basedir}/target/jacoco-coverage-report/html" />
               </report>
           </target>
</configuration>
      </execution>
   </executions>
   <dependencies>
<dependency>
<groupId>org.jacoco</groupId>
<artifactId>org.jacoco.ant</artifactId>
<version>0.7.6.201602180812</version>
</dependency>
   </dependencies>
</plugin>

Run "mvn clean verify", you will see the coverage report under report2/target/jacoco-coverage-report/html.











2 comments :

  1. Cannot find an artifact: Non-resolvable parent POM for jacoco:it-report-aggregate:[unknown-version]: Could not find artifact jacoco:setup-parent:pom:1.0-SNAPSHOT

    must I change settings.xml?

    ReplyDelete
    Replies
    1. The project is configured to have "setup-parent" as it parent, please modify the pom file as I mentioned in this post.

      Delete