简体   繁体   中英

Code coverage in maven build - Skipping JaCoCo execution due to missing classes directory

I am able to get code coverage working fine if I have a single project but now I have a multi module project.

My application is built in the api project and all my integration tests are running in a separate project which uses the artefact built as previous module.

The build runs but I dont get a code coverage report, instead I get the info message:

Skipping JaCoCo execution due to missing classes directory

My coverage report file jacoco-it.exec is created but it seems like the jacoco plugin requires the classes in the project the tests are running in.

Can somebody tell me what I need to do to be able to create a coverage report when the classes are in another module?

I have managed to get a workaround of sorts going through trial and error.

It seems the jacoco plugin is happy to create the exec file without the classes but it will not create the report without them, I dont understand how jacoco is working internally so if someone knows could you please explain it?

I am also not sure if what I have done is reliable but it does seem to report the coverage of my selenium driven tests.

My (possible) solution which I have come up with myself is to use the maven resources plugin to copy the classes which have been exploded from the war file in my target\\cargo.. directory into the directory target\\classes:

 <plugin>
        <artifactId>maven-resources-plugin</artifactId>
        <version>2.7</version>
        <executions>
          <execution>
            <id>copy-resources</id>             
            <phase>pre-integration-test</phase>
            <goals>
              <goal>copy-resources</goal>
            </goals>
            <configuration>
              <outputDirectory>${basedir}/target/classes</outputDirectory>
              <resources>          
                <resource>
                  <directory>${basedir}/target/cargo/configurations/tomcat7x/webapps/calculator-api/WEB-INF/classes</directory>
                  <filtering>false</filtering>
                  <excludes>
                        <exclude>**/*Config*.*</exclude>
                        <exclude>**/*Initialiser*.*</exclude>
                  </excludes>
                </resource>
              </resources>              
            </configuration>            
          </execution>
        </executions>
    </plugin>

This seems to keep the jacoco plugin happy and I get my code coverage, although it seems the plugin ignores my exclude list now.

Does anybody know if this is actually a solution, it 'seems' to work but I cant find anywhere online where this is a recommended approach and I am also unsure of why the exclude option on the jacoco agent set up no longer seems to work.

I have managed to get around the jacoco plugin not excluding files by just not copying them with the resources plugin but I still dont understand exactly how jacoco is working.

Even though jacoco:report-aggregate was meant for aggregation report on the dependencies, it did not work in my case - I use project structure like:

project
└─ module-api   ⇦ API definition
└─ module-spec  ⇦ module with black box tests through the API
└─ module-impl1 ⇦ implementation 1 of the API
└─ module-impl2 ⇦ another implementation of the API
└─ module-test1 ⇦ some dependencies + impl1, apply the tests from spec
└─ module-test2 ⇦ some dependencies + impl2, apply the tests from spec

In this case report-aggregate generated an empty report. The module-test1 and module-test2 projects are with POM packaging type, which is not suitable to run report on them too, as it requires classes in the target/classes directory.

My solution

I configured the jacoco plugin to dump the classes it instruments in the in the project's target/classes directory by adding: <classDumpDir>${project.build.outputDirectory}</classDumpDir>

This allowed the regular report goal to work. The complete code is:

<plugin>
  <groupId>org.jacoco</groupId>
  <artifactId>jacoco-maven-plugin</artifactId>
  <version>0.8.5</version>
  <configuration>
    <destFile>${project.build.directory}/coverage-reports/code-coverage.exec</destFile>
    <dataFile>${project.build.directory}/coverage-reports/code-coverage.exec</dataFile>
    <outputDirectory>${project.reporting.outputDirectory}/code-coverage</outputDirectory>
    <propertyName>jacocoArgLine</propertyName>
    <classDumpDir>${project.build.outputDirectory}</classDumpDir>
  </configuration>
  <executions>
    <execution>
      <id>prepare-jacoco-agent</id>
      <goals>
          <goal>prepare-agent</goal>
      </goals>
    </execution>
    <execution>
      <id>post-integration-test</id>
      <phase>post-integration-test</phase>
      <goals>
        <goal>report</goal>
      </goals>
    </execution>
  </executions>
</plugin>

Please note that the instrumented classes are from all dependencies, so some filtering would be needed to narrow the report scope. See: https://www.eclemma.org/jacoco/trunk/doc/prepare-agent-mojo.html

I have the similar project sructure - main code is in one module and tests in anoter - and got similar problem with jacoco reporting.

My solution 1 :

Just copy /classes folder to test module by maven-resources-plugin .

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-resources-plugin</artifactId>
    <version>3.1.0</version>
    <executions>
       <execution>
         <id>copy-classes-to-test-module</id>
         <goals>
             <goal>testResources</goal>
         </goals>
         <configuration>
             <resources>
                 <resource>
                     <directory>${project.basedir}/../main-module/target/classes</directory>
                 </resource>
             </resources>
             <outputDirectory>
                    ${project.build.outputDirectory}
             </outputDirectory>
          </configuration>
        </execution>
     </executions>
</plugin>

But if your code contains lambdas, anonymous classes or so on, then the result report may not cover some classes, because it will be different classes due to different prepare-agent runs.

My solution 2:

Use the report-aggregate goal.

In parent POM I have a configuration like this:

<plugin>
        <groupId>org.jacoco</groupId>
        <artifactId>jacoco-maven-plugin</artifactId>
        <version>0.8.3</version>
        <configuration>
            <append>true</append>
        </configuration>
        <executions>
            <execution>
                <id>prepare-agent</id>
                <goals>
                    <goal>prepare-agent</goal>
                </goals>
            </execution>
        </executions>
</plugin>

And the test-module's POM contanis these lines:

        <plugin>
            <groupId>org.jacoco</groupId>
            <artifactId>jacoco-maven-plugin</artifactId>
            <executions>
                <execution>
                    <id>report</id>
                    <phase>test</phase>
                    <goals>
                        <goal>report-aggregate</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>

Take in mind that the dependency scope is important for the report-aggregate goal ( jacoco docs ). If your test-module has only test-scoped dependencies, you will got an empty report. Therefore, you should set the scope to compile, runtime or provided for those artifacts, which you want to see in the report. For example:

    <dependency>
        <groupId>my.project</groupId>
        <artifactId>main-module-classes</artifactId>
        <version>${project.version}</version>
        <scope>provided</scope>
    </dependency>

You can use the goal jacoco:report-aggregate which is exactly designed to create coverage reports in a separate test module.

Here is an example :

project
└─ module      ⇦ module with sources
└─ module-test ⇦ module with tests

Then all you have to do is use this goal in the pom.xml of "module-test" :

    <build>
        <plugins>
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>${jacoco.version}</version>
                <executions>
                    <execution>
                        <id>jacoco-report-aggregate</id>
                        <phase>verify</phase>
                        <goals>
                            <goal>report-aggregate</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

Also be careful with the scopes you use in your dependencies as mentioned in the docs .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM