7

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.

While the jar project has its own test cases, I am not interested in executing these. I want to see code coverage in the jar project when executed by the integration tests. Simple, no report aggregation.

user250343
  • 1,163
  • 1
  • 15
  • 24

2 Answers2

6

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.

Godin
  • 9,801
  • 2
  • 39
  • 76
  • Not kind of. It does precisely that. The sentences prior to the one quoted explicitly states so: "The report is created from all modules this project depends on, and optionally this project itself. From those projects class and source files as well as JaCoCo execution data files will be collected. In addition execution data is collected from the project itself". I tried this out and this definitely gives an aggregated coverage value of unit + integration tests. Not the solution if you are looking for coverage using just the integration tests as the question asks for. – Pubudu Aug 09 '22 at 20:08
  • What worked for me was the following approach: https://stackoverflow.com/a/37416679/4329912 – Pubudu Aug 09 '22 at 20:12
5

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.

  • Minor quibble - you need to remove the space before `-runtime.jar` as that's not a separate parameter, that's part of the javaagent. – humanity Mar 22 '19 at 12:20
  • Oh, and simpler to use `JAVA_TOOL_OPTIONS` or `_JAVA_OPTIONS` rather than `JAVA_OPTS` b/c they're automatically picked up. Alternatively, you can just put the `-javaagent` on the `java` command. – humanity Mar 22 '19 at 12:57