Make delicious recipes!

Jacoco report aggregation for code coverage


Jacoco is a an awesome tool for getting the code coverage stats of your project.
Using jacoco's maven plugin, we can generate the code coverage report in just a few lines of pom.xml code.

In this section, we cover how to use the report-aggregate goal to generate coverage reports when the tests are in a separate submodule and the code they test is in some other sibling submodules. Consider the following project structure:
./pom.xml

./submodule1/pom.xml
./submodule1/src/main/java/prismoskills/Foo.java

./submodule2/pom.xml
./submodule2/src/main/java/prismoskills/Bar.java

./tests/pom.xml
./tests/src/test/java/prismoskills/TestFooBar.java
This has a parent pom.xml with 3 submodules:
  1. submodule1
  2. submodule2
  3. tests



If we use Jacoco-maven-plugin directly in all the submodules, then we will not get any coverage from the tests submodule because that module does not contain any code. All the code is present in other submodules. Hence we need to use the report-aggregate goal.


First the Java files:
  1. submodule1/src/main/java/prismoskills/Foo.java
    package prismoskills;
    
    public class Foo {
    
      public void calculate(int c) {
          if (c > 10)
              c = c / 2;
          else
              c = c * 2;
    
          for (int i=0; i<c; i++) {
              if (i < 5) {
                  System.out.println("Hello " + i);
              } else {
                  System.out.println("Hi " + i);
              }
          }
      }
    }
    
    
  2. submodule2/src/main/java/prismoskills/Bar.java (Pretty much a copy-paste of Foo.java)
    package prismoskills;
    
    public class Bar {
    
      public void calculate(int c) {
          if (c > 10)
              c = c / 2;
          else
              c = c * 2;
    
          for (int i=0; i<c; i++) {
              if (i < 5) {
                  System.out.println("Hello " + i);
              } else {
                  System.out.println("Hi " + i);
              }
          }
      }
    }
    
  3. tests/src/test/java/prismoskills/TestFooBar.java
    package prismoskills;
    
    import org.junit.Test;
    
    public class TestFooBar {
    
      @Test
      public void test() {
        new Foo().calculate(6);
        new Bar().calculate(2);
      }
    }
    



Now we enable the jacoco-maven-plugin for all the modules by declaring it in the parent pom.xml file as shown below:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>


  <groupId>prismoskills</groupId>
  <artifactId>jacoco-report-aggregate-example</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <packaging>pom</packaging>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.8.2</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <modules>
    <module>submodule1</module>
    <module>submodule2</module>
    <module>tests</module>
  </modules>

  <build>

   <pluginManagement>
      <plugins>
        <plugin>
          <groupId>@project.groupId@</groupId>
          <artifactId>jacoco-maven-plugin</artifactId>
          <version>@project.version@</version>
        </plugin>
      </plugins>
    </pluginManagement>

    <plugins>
      <plugin>
        <groupId>org.jacoco</groupId>
        <artifactId>jacoco-maven-plugin</artifactId>
        <executions>
          <execution>
            <id>prepare-agent</id>
            <goals>
              <goal>prepare-agent</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
</project>
Note the use of prepare-agent goal in the above. This goal prepares a property pointing to the JaCoCo runtime agent that can be passed as a VM argument to the application under test. Now whenever java is run to execute tests, this VM argument will be passed to it and enable Jacoco instrumentation.

The submodules remain blissfully unaware of this change and their pom.xmls remain unchanged:

submodule1/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>prismoskills</groupId>
    <artifactId>jacoco-report-aggregate-example</artifactId>
    <version>1.0.0-SNAPSHOT</version>
  </parent>

  <artifactId>submodule1</artifactId>
</project>

submodule2/pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>prismoskills</groupId>
    <artifactId>jacoco-report-aggregate-example</artifactId>
    <version>1.0.0-SNAPSHOT</version>
  </parent>

  <artifactId>submodule2</artifactId>
</project>


And finally the magic happens in tests/pom.xml as:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>prismoskills</groupId>
    <artifactId>jacoco-report-aggregate-example</artifactId>
    <version>1.0.0-SNAPSHOT</version>
  </parent>

  <artifactId>tests</artifactId>

  <dependencies>
    <dependency>
      <groupId>prismoskills</groupId>
      <artifactId>submodule1</artifactId>
      <version></version>
    </dependency>
    <dependency>
      <groupId>prismoskills</groupId>
      <artifactId>submodule2</artifactId>
      <version></version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.19.1</version>
              <configuration>
                  <!-- Jacoco prepare-agent builds some command-line params without -->
                  <!-- which jacoco will not instrument. Hence it is important to add -->
                  <!-- those command-line params here (${argLine} holds those params) -->
                  <argLine>${argLine} -Xms256m -Xmx2048m</argLine>
                  <forkCount>1</forkCount>
                  <runOrder>random</runOrder>
              </configuration>
        </plugin>
      <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>

</project>


Run mvn clean install at the top-level.
And you can see that a report is generated in tests/target/site/jacoco-aggregate/index.html like the following:


The summary page here shows the code coverage of the tests according to package.
Percent of code covered is highlighted in green and also mentioned as a number. 100% is the ideal number to strive for.

You can drill down the links to see complete line-by-line coverage as shown below:
See how the coverage for the two classes differ because the test we wrote called them with different values of c.
public class TestFooBar {
  @Test
  public void test() {
    new Foo().calculate(6);
    new Bar().calculate(2);
  }
}


With such a coverage, now we can become sure what lines in our code have been tested.
It also tells us what and how much of the untested code would go to production (and hence execute for the first time).








Like us on Facebook to remain in touch
with the latest in technology and tutorials!


Got a thought to share or found a
bug in the code?
We'd love to hear from you:

Name:
Email: (Your email is not shared with anybody)
Comment:

Facebook comments:

Site Owner: Sachin Goyal