DevOps Zone is brought to you in partnership with:

I'm a software developer for whom coding is a way of life as well as a hobby. Focused on slimming enterprise class systems, strongly interested in new technologies and continuously improving the software delivery process. My great passion are lightweight applications, in which the ratio of provided functionality to the code is as smallest as possible. Co-founder of the agile software house Codearte. Confitura conference organizer. Jakub is a DZone MVB and is not an employee of DZone and has posted 14 posts at DZone. You can read more from them at their website. View Full User Profile

Measuring Overall Code Coverage in Multi-Module Maven Project

04.01.2013
| 3834 views |
  • submit to reddit
As we know unfortunately Maven has no out of the box support for different test types. There are in fact few open tickets for adapting Maven to different test strategies, like adding integrationTestSourceDirectory to POM model (MNG-2009) or new lifecycle phases for operating on integration test sources (MNG-2010) to replace necessity of using build-helper-maven-plugin.

But apart from separating varying tests from each other how can we use available mechanisms to invoke both unit and integration tests and measure theirs code coverage? I want to achieve such situation (maybe except the number of tests because I usually want to have a little bit more :)) on the Sonar dashboard:

The first thing we have to do is to enable proper Sonar plugin. To do it just click on the dashboard's "Configure widgets", select "Integration tests coverage" item and place it in the appropriate position.

We'll work on a simple maven project with the popular layout:

Both modules contains two "production" classes: one to be tested using unit tests and other for integration testing. Structure of the project looks like below (I hope class names are pretty obvious):

Now it's time to adjust our project to handle suitable coverage measurement tool. I prefer JaCoCo because it is well integrated with Sonar and can be adapted to work with different test types. As a test framework we'll use TestNG. So let's add to our master pom.xml following dependencies:
   <dependencies>
    <dependency>
      <groupid>org.testng</groupid>
      <artifactid>testng</artifactid>
      <version>6.7</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupid>org.jacoco</groupid>
      <artifactid>org.jacoco.core</artifactid>
      <version>0.6.2.201302030002</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
In TestNG we can combine tests in groups by using @Test(group="groupName"), and we're going to use this functionality and use "unit" group for all unit tests and "int" group for integration ones.

We also must set sonar properties:
  <properties>
    <!-- select JaCoCo as a coverage tool -->
    <sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin>
    <!-- force sonar to reuse reports generated during build cycle -->
    <sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
    <!-- set path for unit tests reports -->
    <sonar.jacoco.reportPath>${project.basedir}/target/jacoco-unit.exec</sonar.jacoco.reportPath>
    <!-- all modules have to use the same integration tests report file -->
    <sonar.jacoco.itReportPath>${project.basedir}/../target/jacoco-it.exec</sonar.jacoco.itReportPath>
  </properties>
The next thing we've to do is to configure Maven plugins. To the standard surefire configuration we'll add exclusion of the "int" tests group:
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>2.14</version>
        <configuration>
          <excludedGroups>int</excludedGroups>
        </configuration>
      </plugin>
Now a little bit more complex situation with configuring failsafe plugin. Failsafe is more suitable for integration tests because when a test fails, it does not immediately abort (instead it lets the clean-up by processing post-integration-test phase).
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-failsafe-plugin</artifactId>
        <version>2.14</version>
        <configuration>
          <!-- property set by jacoco-maven-plugin -->
          <argLine>${itCoverageAgent}</argLine>
          <groups>int</groups>
          <!-- by default only IT*, *IT and *ITCase classes are included -->
          <includes>
            <include>**/*.java</include>
          </includes>
        </configuration>
        <executions>
          <execution>
            <id>integration-test</id>
            <goals>
              <goal>integration-test</goal>
              <goal>verify</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
The last step is adding jacoco plugin:
      <plugin>
        <groupId>org.jacoco</groupId>
        <artifactId>jacoco-maven-plugin</artifactId>
        <version>0.6.2.201302030002</version>
        <executions>
          <!-- prepare agent for measuring unit tests -->
          <execution>
            <id>prepare-unit-tests</id>
            <goals>
              <goal>prepare-agent</goal>
            </goals>
            <configuration>
              <destFile>${sonar.jacoco.reportPath}</destFile>
            </configuration>
          </execution>

          <!-- prepare agent for measuring integration tests -->
          <execution>
            <id>prepare-integration-tests</id>
            <goals>
              <goal>prepare-agent</goal>
            </goals>
            <phase>pre-integration-test</phase>
            <configuration>
              <destFile>${sonar.jacoco.itReportPath}</destFile>
              <propertyName>itCoverageAgent</propertyName>
            </configuration>
          </execution>
        </executions>
      </plugin>
Now invoke normal maven build (by typing mvn clean install). We can see in the compilation output that JaCoCo plugin is invoked both for unit
[INFO] --- jacoco-maven-plugin:0.6.2.201302030002:prepare-agent (prepare-unit-tests) @ first ---
and integration tests
[INFO] --- jacoco-maven-plugin:0.6.2.201302030002:prepare-agent (prepare-integration-tests) @ first ---
While build finished successful we can proceed to running Sonar: mvn sonar:sonar

I hope everything went ok, and now you can admire well measured code coverage on your Sonar dashboard!
Published at DZone with permission of Jakub Kubrynski, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)