I am following the standard Maven pattern where I use a separate module for integration tests. This module has a wrapper class that executes the major dependent jar project.
I have attached a Java agent to the application server's standalone.sh (startup script) by adding the line below:
JAVA_OPTS="$JAVA_OPTS -javaagent:/home/jboss/.m2/repository/org/jacoco/org.jacoco.agent/0.7.6.201602180812/org.jacoco.agent-0.7.6.201602180812 -runtime.jar=destfile=/var/lib/jenkins/workspace/HDAP_JaCoCo/jacocoSoapui.exec,includes=*,append=false,output=file"
I have specified the destination file to be located in the workspace of my Jenkins job which runs JaCoco code coverage for unit tests (in order to get the classes to compare my coverage with).
Then I have specified the path of the two exec files (one from the unit tests, and the one that I have created for integration tests in the record coverage report section of the Jenkins job mentioned above). Then we have the coverage for all tests now.
Note:
The app server needs to be stopped to dump the coverage onto the exec file. Let me know if you have any more questions.
When you want to get coverage for specific set of tests, make sure that nothing else is running because this will give you coverage for basically any calls being made in to the application.
Let me quote http://www.jacoco.org/jacoco/trunk/doc/report-aggregate-mojo.html even if its name contains a kind of "aggregation":
This also allows to create coverage reports when tests are in separate projects than the code under test, for example in case of integration tests.
Let's try. Given jar/src/main/java/example/Example.java
:
package example;
public class Example {
// to be covered by unit test
public void a() {
System.out.println("a");
}
// to be covered by integration test
public void b() {
System.out.println("b");
}
}
unit test jar/src/test/java/example/ExampleTest.java
:
package example;
public class ExampleTest {
@org.junit.Test
public void test() {
new Example().a();
}
}
integration test it/src/test/java/example/ExampleITTest.java
:
package example;
public class ExampleITTest {
@org.junit.Test
public void test() {
new Example().b();
}
}
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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>example</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<modules>
<module>jar</module>
<module>it</module>
</modules>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.8</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
jar/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>org.example</groupId>
<artifactId>example</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>jar</artifactId>
</project>
and finally most important part it/pom.xml
where happens all the magic:
<?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>org.example</groupId>
<artifactId>example</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>it</artifactId>
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>jar</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<executions>
<!--
"report" goal can't cross boundaries of modules,
while "report-aggregate" can, so let's use it, however
by default it will load "jacoco.exec" from this module and from module "jar",
so let's also change file name for this module to avoid intersection
-->
<execution>
<configuration>
<destFile>${project.build.directory}/jacoco-it.exec</destFile>
</configuration>
</execution>
<execution>
<id>it-report</id>
<phase>verify</phase>
<goals>
<goal>report-aggregate</goal>
</goals>
<configuration>
<dataFileIncludes>**/jacoco-it.exec</dataFileIncludes>
<outputDirectory>${project.reporting.outputDirectory}/jacoco</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
In such setup mvn clean verify
will generate two reports - jar/target/site/jacoco
showing that method a()
is covered, and it/target/site/jacoco
showing that method b()
is covered.