JaCoCo is a test coverage analysis tool and works conveniantly when used to measure test coverage within a single maven module. However, it is a common practice to isolate integration tests in a dedicated module – for instance to set up common testing infrastructure and mocks before executing the integration tests.
If both the tested classes and their sources do not reside in the same module as the tests, the jacoco-maven-plugin fails like so (as of version 0.6.2.201302030002):
basedir <module directory>/target/classes does not exist
To solve this, one must understand that JaCoCo’s coverage measurement is a two-step process: First, coverage data is obtained by attaching a java agent to the process executing the tests. This agent instruments loaded classes at runtime and generates a data file containing the coverage measurements (e.g. jacoco.exec). Second, a report is generated by matching the collected data against a set of classes (the module classes compiled from src/main/). This report also utilizes the sources from src/main to let you navigate through the coverage results for each class. Thus, the only thing missing are the covered classes and sources for report generation. These cannot be obtained by JaCoCo as it is impossible to determine which elements of the classloader are targeted by the integration tests (after all, this is project-specific and usually implicit). In order to generate the report one must thus explicitly define the tested dependencies and sources. This is easily feasible using the maven-dependency-plugin in conjunction with the maven-source plugin:
Step 1: make the tested modules provide their sources:
org.apache.maven.plugins maven-source-plugin attach-sources jar
Step 2: Unpack the classes and sources you want in the report using the maven-dependency-plugin:
maven-dependency-plugin prepare-covered-classes unpack-dependencies generate-resources sources ${build.directory}/covered-classes prepare-covered-sources unpack-dependencies generate-resources sources ${build.directory}/covered-sources group.id.of.covered.package[,another.group.id]
Step 3: Execute JaCoCo using the custom classes and sources
Unfortunately, this step requires executing JaCoCo using ANT, as the maven plugin does not (yet) support the required configurations (custom classFiles and sourceFiles):
maven-antrun-plugin default-cli post-integration-test run org.jacoco org.jacoco.ant 0.6.2.201302030002
Note: the <id> name “default-cli” is required in order to execute the ant task directly via
mvn ant:run
This solution has a couple of advantages over many of the solutions for the same problem I found on the web: It is independent of the directory structure. It scales well as arbitrary further modules can be unpacked by the dependency plugin without specific configuration. It does not attempt to merge unit and integration test coverage results – which would involve knowing the directory structure and mixes up tests with different semantics and intends. Finally, this approach does not introduce ugly dependency cycles which arise if one tries to embed integration test coverage results in the coverage reports of the tested modules – this would require the tested modules to be build first, followed by the integration module(s), with another subsequent build of the tested modules to generate a report based on the merged results from both unit- and integrationtests.