Friday, 3 June 2011

Separating Code Coverage With Maven, Sonar and Jacoco

1. Introduction
In this example I will expand upon my previous example of keeping your unit and integration tests in separate packages, and explain how to also produce code coverage statistics.

For this I continue to use Maven, whilst adding Sonar and Jacoco to provide the code coverage.
You can run the example below, but you will need to read the first article here to understand it.

http://johndobie.blogspot.com/2011/06/seperating-maven-unit-integration-tests.html

Update

There is a new, simpler example of how to do this here
http://johndobie.blogspot.com/2012/05/easy-unit-and-integration-code-coverage.html


1.1 Example Structure

Here we have the typical maven structure and our new folder \src\integrationtest\java


1.2 Running the Example
The full code is hosted at google code.  Use the following commands to check it out and run it.
svn co https://designbycontract.googlecode.com/svn/trunk/sonar
cd sonar
mvn –Psonar,it clean install sonar:sonar
You need to have sonar running.  Instructions on the install can be found here.
http://docs.codehaus.org/display/SONAR/Install+Sonar

Go to sonar web interface and click on 'configure widgets' to add the IT coverage widget
You will also have to add the Jacoco plugin.

You can see the Jenkins build here:
http://ec2-75-101-221-43.compute-1.amazonaws.com:8080/job/maven-sonar-jaccoco/

You can see the Sonar build here
http://ec2-75-101-221-43.compute-1.amazonaws.com:9000/dashboard/index/1

1.3  Results of running the example
  • The tests in the standard maven test structure are run during the unit test phase as usual.
  • The tests in the integrationtest directory are run during the integration test phase.
  • The test classes and results are placed in a seperate integrationtest directory in target


The jacoco test coverage reports are placed in the /target/coverage-reports directory


In Sonar, the unit test coverage is shown in one widget and the integration test coverage in another.



2.0 How does it work?
The basic steps are as follows. The details for each are below.
  • Run the maven integration-test lifecycle with Jacoco coverage enabled for unit and integration tests.
  • Run Sonar pointing to the coverage results.
  • Put it all in a profile so it can be used conveniently.

2.1  Running the tests with Jacoco
We first define the directories for the jacoco coverage files.
<coverage.reports.dir>
  ${basedir}/target/coverage-reports
</coverage.reports.dir>
<sonar.jacoco.reportPath>
  ${coverage.reports.dir}/jacoco-unit.exec
</sonar.jacoco.reportPath>
<sonar.jacoco.itReportPath>
  ${coverage.reports.dir}/jacoco-it.exec
</sonar.jacoco.itReportPath>
<sonar.jacoco.jar>
  ${basedir}/lib/jacocoagent.jar
</sonar.jacoco.jar>
Then we start the unit tests by running the standard the surefire plugin with the Jacoco agent pointing to ${sonar.jacoco.reportPath}.  This is used to store the unit test code coverage results.
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.7.2</version>
    <configuration>
    <argLine>          
       -javaagent:${sonar.jacoco.jar}=destfile=${sonar.jacoco.reportPath}       
    </argLine>
    <test>**/*.java</test>
    </configuration>
</plugin>
For the Integration tests we use the failsafe plugin and point the Jacoco agent to ${sonar.jacoco.itReportPath}.  This is used to store the integration test code coverage results.
<plugin>
  <artifactId>maven-failsafe-plugin</artifactId>
  <version>2.8</version>
  <configuration>         
    <argLine>
      -javaagent:${sonar.jacoco.jar}=destfile=${sonar.jacoco.itReportPath}
    </argLine>
   <testClassesDirectory>
      ${integrationOutputDirectory}
   </testClassesDirectory>
   <reportsDirectory>
     ${integrationOutputDirectory}/failsafe-reports
   </reportsDirectory>
   <test>**/*.java</test>
   <additionalClasspathElements>
     <additionalClasspathElement>
       ${integrationSourceDirectory}/resources</additionalClasspathElement>
     </additionalClasspathElements>
   </configuration>
   <executions>
     <execution>
       <goals>
          <goal>integration-test</goal>
       </goals>
     </execution>
  </executions>
</plugin>

When these are run they will produce the following 2 coverage files.


Running Sonar
We use the standard sonar:sonar target however we first compile and run our tests.
This will cause the unit and integration tests to run as described above.
The following parameter tells Sonar to use jacoco as the default code coverage plugin
<sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin>
We then specify for Sonar to re-use the created coverage files.
<sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
We then run the whole process by running the following Maven command
mvn –Psonar,it clean install sonar:sonar
This produces the results discussed above in section 1.3. Results Of Running the Examples

2.3  Using a Profile
The sonar profile tidies all of these pieces up into one reusable unit. The full profile is here.
https://designbycontract.googlecode.com/svn/trunk/sonar/pom.xml

2.4  Next Steps
You can look at a multi-module example of the same technique here:
 http://johndobie.blogspot.com/2011/06/maven-multi-module-with-sonar-and.html

2 comments:

  1. thanks for the post -> was a great help

    ReplyDelete
  2. Great post, you helped me to configure all this. Thank you John. Cheers, Tomasz Kusmierczyk

    ReplyDelete