Integration Tests with Maven (Part 2): Test Coverage Reports


June 23, 2009 By Marvin Froeder

In the previous article, we demonstrated one way of running integration tests with Maven. In this article, we will demonstrate how to measure the test coverage of both integration and unit tests.

We will be using Emma to measure and report the coverage. EMMA is an open-source toolkit for measuring and reporting Java code coverage.

Unit test coverage

Let’s start by generating a coverage report for unit tests only. This will require 2 new plugins to be added to our pom. The first one:

<plugin>
  <groupId>org.sonatype.maven.plugin</groupId>
  <artifactId>emma-maven-plugin</artifactId>
  <version>1.1</version>
  <executions>
    <execution>
      <phase>process-classes</phase>
      <goals>
        <goal>instrument</goal>
      </goals>
    </execution>
  </executions>
</plugin>

This plugin will instrument classes at target/generated-class/emma/classes, this won’t change the original classes at target/classes. Then, we must tell surefire plugin to look for classes at new location:

  org.apache.maven.plugins
  maven-surefire-plugin
  2.4.3
 
    ${project.build.directory}/generated-classes/emma/classes

At this point, we do 50% of code coverage. Mainly because the psvm ( public static void main ) is never executed.

Here is the EMMA text report. Emma does produce xml and html reports in addition to this simple text report:

[EMMA v2.0.5312 report, generated Fri Jun 12 15:43:28 BRT 2009]
-------------------------------------------------------------------------------
OVERALL COVERAGE SUMMARY:
 
[class, %]	[method, %]	[block, %]	[line, %]	[name]
100% (1/1)	67%  (2/3)!	56%  (10/18)!	50%  (4/8)!	all classes

Note: at this point no report is generated, we will configure report generation after integration tests coverage reports are in place. Once you run Surefire, you may notice the coverage.ec in the project root directory. This file contains the coverage result in a binary format used by Emma.

Integration test coverage

So far, we have the unit tests results. As I demonstrated in the previous article in this series, the integration tests need the project artifact to run, so, we need to instrument this jar in order to get coverage result on ITs. Let’s see the pom:

  org.sonatype.maven.plugin
  emma4it-maven-plugin
  1.3
 
      instrument
 
        instrument-project-artifact
 
        true

Note that is using a different plugin: emma4it-maven-plugin. This plugin provides some additional goals for instrumenting integrations tests. For this project we use instrument-project-artifact goal. This goal will add Emma instrumentation into the project jar. This is specific to tests running on integration-test phase, because it does requires that the packaged artifact be instrumented.

There is also the configuration appendEmma. When true, this goal will shade the Emma jar into the instrumented jar. This is very useful to test jars that will be launched from command line, like this project.

[EMMA v2.0.5312 report, generated Fri Jun 12 16:05:45 BRT 2009]
-------------------------------------------------------------------------------
OVERALL COVERAGE SUMMARY:
 
[class, %]	[method, %]	[block, %]	[line, %]	[name]
100% (1/1)	100% (3/3)	89%  (16/18)	88%  (7/8)	all classes

At this point we have a very decent code coverage, almost 90%, 7 of 8 lines of code, and on this project it is not possible to get 8/8. And why is that? System.exit(). when this line is invoked nothing else is executed, that includes Emma instrumented code, so Emma won’t be able to know if System.exit() was executed, our integration tests catch that, because we check the exit code of the process, but Emma can’t do the same.

Test Coverage Report

At this point we have all coverage data stored at coverage.ec, in Emma binary format. Let’s now convert that into something that is more readable.

Add the following execution on emma4it-maven-plugin:

  report
post-integration-test
 
    report
 
        ${project.build.sourceDirectory}

Now the text, HTML, and XML reports are generated at target/site/emma.

Emma HTML coverage report

Emma HTML coverage report

That is it for part 2. On the next part, I will show some ideas about testing more complex projects like flexmojos and nexus.

  • James

    Thank you for a very informative article. I have been finding it difficult to find information on combining code coverage for integration tests. However, when I follow your instructions I get the following error when I run any maven build, such as mvn clean install, or mvn emma:emma. What command should I be using?

    Thanks,

    James.

    [INFO] task-segment: [clean, install, emma:emma]
    [INFO] ————————————————————————
    [INFO] [clean:clean {execution: default-clean}]
    [INFO] [emma:instrument {execution: default}]
    [INFO] Not executing EMMA, as the project is not a Java classpath-capable package
    [INFO] [site:attach-descriptor {execution: default-attach-descriptor}]
    [INFO] [emma4it:instrument-project-artifact {execution: instrument}]
    [INFO] ————————————————————————
    [ERROR] BUILD ERROR
    [INFO] ————————————————————————
    [INFO] Unable to find project artifact file!
    [INFO] ————————————————————————
    [INFO] Trace
    org.apache.maven.lifecycle.LifecycleExecutionException: Unable to find project artifact file!
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:719)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalWithLifecycle(DefaultLifecycleExecutor.java:556)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoal(DefaultLifecycleExecutor.java:535)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalAndHandleFailures(DefaultLifecycleExecutor.java:387)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeTaskSegments(DefaultLifecycleExecutor.java:348)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.execute(DefaultLifecycleExecutor.java:180)
    at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:328)
    at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:138)
    at org.apache.maven.cli.MavenCli.main(MavenCli.java:362)
    at org.apache.maven.cli.compat.CompatibleMain.main(CompatibleMain.java:60)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.codehaus.classworlds.Launcher.launchEnhanced(Launcher.java:315)
    at org.codehaus.classworlds.Launcher.launch(Launcher.java:255)
    at org.codehaus.classworlds.Launcher.mainWithExitCode(Launcher.java:430)
    at org.codehaus.classworlds.Launcher.main(Launcher.java:375)
    Caused by: org.apache.maven.plugin.MojoExecutionException: Unable to find project artifact file!
    at org.sonatype.maven.plugin.emma4it.InstrumentProjectArtifactMojo.execute(InstrumentProjectArtifactMojo.java:115)
    at org.apache.maven.plugin.DefaultPluginManager.executeMojo(DefaultPluginManager.java:490)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:694)
    … 17 more
    [INFO] ————————————————————————
    [INFO] Total time: 1 minute 27 seconds
    [INFO] Finished at: Fri Apr 16 14:30:13 BST 2010
    [INFO] Final Memory: 72M/129M
    [INFO] ————————————————————————