0

I'm trying to setup jacoco to get the coverage for my integration tests. I'm running my integration tests against jetty (using the maven plugin). But even if i pass the agent in the jam args when starting up the jetty server the jacoco report shows 0%. Here is my pom.xml

<groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-maven-plugin</artifactId>
        <executions>
          <execution>
            <id>start-jetty</id>
            <phase>pre-integration-test</phase>
            <goals>
              <goal>run-forked</goal>
            </goals>
            <configuration>
              <waitForChild>false</waitForChild>
              <jvmArgs>-Denv=it -Djetty.port=8081 ${failsafeArgLine}</jvmArgs>
              <webApp>
                <contextPath>/myContext</contextPath>
              </webApp>
            </configuration>
          </execution>
          <execution>
            <id>stop-jetty</id>
            <phase>post-integration-test</phase>
            <goals>
              <goal>stop</goal>
            </goals>
          </execution>
        </executions>
        <configuration>
          <stopPort>8082</stopPort>
          <stopKey>test</stopKey>
        </configuration>
      </plugin>

<plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-failsafe-plugin</artifactId>
        <version>2.12.4</version>
        <configuration>
          <includes>
            <include>**/*IntegrationTest.java</include>
          </includes>
        </configuration>
        <executions>
          <execution>
            <goals>
              <goal>integration-test</goal>
              <goal>verify</goal>
            </goals>
            <configuration>
              <argLine>${failsafeArgLine}</argLine>
            </configuration>
          </execution>
        </executions>
      </plugin>

<plugin>
        <groupId>org.jacoco</groupId>
        <artifactId>jacoco-maven-plugin</artifactId>
        <executions>

          <execution>
            <id>pre-integration-test</id>
            <phase>pre-integration-test</phase>
            <goals>
              <goal>prepare-agent</goal>
            </goals>
            <configuration>

              <destFile>${project.build.directory}/coverage-reports/jacoco-it.exec</destFile>

              <propertyName>failsafeArgLine</propertyName>
            </configuration>
          </execution>

          <execution>
            <id>post-integration-test</id>
            <phase>post-integration-test</phase>
            <goals>
              <goal>report</goal>
            </goals>
            <configuration>

              <dataFile>${project.build.directory}/coverage-reports/jacoco-it.exec</dataFile>

              <outputDirectory>${project.reporting.outputDirectory}/jacoco-it</outputDirectory>
            </configuration>
          </execution>

        </executions>
      </plugin>

As you can see i'm running the jetty in fork mode and I do pass the jacoco agent in the params, but nothing...

Is there something extra I need to add?

Johny19
  • 5,364
  • 14
  • 61
  • 99
  • 1
    Where are you passing the jacocoagent.jar file while passing the agent's args. Also, you might need to stop your instance (jetty) so that it can flush the coverage data to the jacoco-it.exec file. After Jetty will stop, if your tests ran (with success or some failure), you'll see jacoco-it.exec file size will grow more and then jacoco will be able to generate %. – AKS Nov 11 '15 at 18:24
  • Where Am I passing them? in the start-jetty (-Denv=it -Djetty.port=8081 ${failsafeArgLine}) I can see this line in the maven logs - failsafeArgLine set to -javaagent:/mypath/repository/org/jacoco/org.jacoco.agent/0.7.5.201505241946/org.jacoco.agent-0.7.5.201505241946-runtime.jar=destfile=/targetpath/keepmypet/target/coverage-reports/jacoco-it.exec – Johny19 Nov 11 '15 at 19:38

2 Answers2

4

There are lots answers out there from people that have read the help and replied without any idea of what really takes to make it work.

So were we go..

First things first. Maven run things based on the phases those things are bound to, however if you have two things bound to the same phase Maven will execute them in the order they appear on the POM file, so in this case order matters.

Here is how my POM file looks like:

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.17</version>
            <configuration>
                <argLine>${surefireArgLine}</argLine>
                <skipTests>${skip.unit.tests}</skipTests>
                <excludes>
                    <exclude>**/*TestSuite*</exclude>
                    <exclude>**/*ITest*</exclude>
                </excludes>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-failsafe-plugin</artifactId>
            <version>2.12</version>
            <configuration>
                <skipTests>${skip.integration.tests}</skipTests>
                <includes>
                    <include>**/*TestSuite*</include>
                    <include>**/*ITest*</include>
                </includes>
                <systemPropertyVariables>
                     ... //Whatever you need to configure on your integration test, if any.
                </systemPropertyVariables>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.jacoco</groupId>
            <artifactId>jacoco-maven-plugin</artifactId>
            <version>0.7.5.201505241946</version>
            <executions>
                <execution>
                    <id>pre-unit-test</id>
                    <goals>
                        <goal>prepare-agent</goal>
                    </goals>
                    <configuration>
                        <destFile>${project.build.directory}/coverage-reports/jacoco-ut.exec</destFile>
                        <excludes>
                            <exclude>**/*Test*</exclude>
                        </excludes>
                        <propertyName>surefireArgLine</propertyName>
                    </configuration>
                </execution>
                <execution>
                    <id>post-unit-test</id>
                    <goals>
                        <goal>report</goal>
                    </goals>
                    <configuration>
                        <dataFile>${project.build.directory}/coverage-reports/jacoco-ut.exec</dataFile>
                        <outputDirectory>${project.reporting.outputDirectory}/jacoco-ut</outputDirectory>
                    </configuration>
                </execution>
                <execution>
                    <id>pre-integration-test</id>
                    <phase>pre-integration-test</phase>
                    <goals>
                        <goal>prepare-agent</goal>
                    </goals>
                    <configuration>
                        <destFile>${project.build.directory}/coverage-reports/jacoco-it.exec</destFile>
                        <excludes>
                            <exclude>**/*Test*</exclude>
                        </excludes>
                        <propertyName>jacoco.agent.itArgLine</propertyName>
                    </configuration>
                </execution>
                <execution>
                    <id>post-integration-test</id>
                    <phase>verify</phase>
                    <goals>
                        <goal>report</goal>
                    </goals>
                    <configuration>
                        <dataFile>${project.build.directory}/coverage-reports/jacoco-it.exec</dataFile>
                        <outputDirectory>${project.reporting.outputDirectory}/jacoco-it</outputDirectory>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-maven-plugin</artifactId>
            <version>9.2.10.v20150310</version>
            <configuration>
                <scanIntervalSeconds>10</scanIntervalSeconds>
                <stopKey>foo</stopKey>
                <stopPort>9999</stopPort>
                <httpConnector>
                    <port>${it.server.port}</port>
                </httpConnector>
                <contextXml>jetty_context.xml</contextXml>
            </configuration>
            <executions>
                <execution>
                    <id>start-jetty</id>
                    <phase>pre-integration-test</phase>
                    <goals>
                        <goal>run-forked</goal>
                    </goals>
                    <configuration>
                        <scanIntervalSeconds>0</scanIntervalSeconds>
                        <daemon>true</daemon>
                        <waitForChild>false</waitForChild>
                        <maxStartupLines>200</maxStartupLines>
                        <jvmArgs>${jacoco.agent.itArgLine} -Djetty.port=${it.server.port}</jvmArgs>
                    </configuration>
                </execution>
                <execution>
                    <id>stop-jetty</id>
                    <phase>post-integration-test</phase>
                    <goals>
                        <goal>stop</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>

That is an actual working POM.xml configuration. Now lets go to the important details on that:

  1. Jacoco plugin must proceed Jetty plugin, otherwise you will have no property with the javaagent configuration
  2. You must run Jetty forked, otherwise Jetty wont take the javaagent configuration.
  3. Since you run Jetty forked you cant use <webApp>...</webApp> to define the context path. You will need a context.xml file for that. I'm using one for Jetty and another for Tomcat (production). See contents below.
  4. Jacoco reports on integration tests cannot be bound to post-integration-test phase, otherwise you will report results before actually collecting them. (Assuming you want Jacoco to report, if you don't skip this). So you have to bind it to the next available phase that would be verify.
  5. The maxStartupLines is used to let Jetty know how many initialization lines it may ignore before assuming there was an initialization error. You may need to customize that.

Context.xml (jetty_context.xml):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN"     "http://www.eclipse.org/jetty/configure_9_0.dtd">
<Configure class="org.eclipse.jetty.webapp.WebAppContext">
  <Set name="contextPath">/myApp</Set>
  <Set name="extraClasspath">comma-separated list of additional classpaths, such as resources</Set>
</Configure>

In case you want to also report you code coverage to SonarQube:

<properties>
    <sonar.jacoco.reportPath>${project.build.directory}/coverage-reports/jacoco-ut.exec</sonar.jacoco.reportPath>
    <sonar.jacoco.itReportPath>${project.build.directory}/coverage-reports/jacoco-it.exec</sonar.jacoco.itReportPath>
</properties>

If everything else is correct then you should be able to run: mvn clean install sonar:sonar

1

I did some work with setting this up myself the last couple of days and have a working example here:

https://github.com/daniellundmark/jetty-jacoco-example

Maybe that can help you.

When running your example above, I got this error:

[INFO] Forked process starting
[INFO] Forked process startup errors

I got your example working (with some recorded coverage) by changing to prepare-agent-integration, report-integration and moving the plugin before the jetty plugin in the pom file.

It was useful to run mvn -X pre-integration-test to see what happened. But have a look at the example above as well. Hopefully that can help you.

Here are the plugins I ended up with in the pom.xml file when changing your example:

            <plugin>
            <groupId>org.eclipse.jetty</groupId>
            <artifactId>jetty-maven-plugin</artifactId>
            <executions>
                <execution>
                    <id>start-jetty</id>
                    <phase>pre-integration-test</phase>
                    <goals>
                        <goal>run-forked</goal>
                    </goals>
                    <configuration>
                        <waitForChild>false</waitForChild>
                        <jvmArgs>${failsafeArgLine} -Denv=it -Djetty.port=8081</jvmArgs>
                        <webApp>
                            <contextPath>/myContext</contextPath>
                        </webApp>
                    </configuration>
                </execution>
                <execution>
                    <id>stop-jetty</id>
                    <phase>post-integration-test</phase>
                    <goals>
                        <goal>stop</goal>
                    </goals>
                </execution>
            </executions>
            <configuration>
                <stopPort>8082</stopPort>
                <stopKey>test</stopKey>
            </configuration>
        </plugin>

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-failsafe-plugin</artifactId>
            <version>2.12.4</version>
            <configuration>
                <includes>
                    <include>**/*IT.java</include>
                </includes>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>integration-test</goal>
                        <goal>verify</goal>
                    </goals>
                    <configuration>
                        <argLine>${failsafeArgLine}</argLine>
                    </configuration>
                </execution>
            </executions>
        </plugin>
Daniel Lundmark
  • 2,370
  • 16
  • 11
  • I noticed it has two goals in the github example at https://github.com/daniellundmark/jetty-jacoco-example/blob/master/pom.xml whereas the above code doesn't show this. I suppose the code above is the correct one. – Stephane Dec 18 '15 at 11:12
  • With this configuration, you still have your jetty starting ? – Stephane Dec 18 '15 at 11:19
  • Don't you need to have your goals as: `startrun-forked` ? – Stephane Dec 18 '15 at 11:21
  • 1
    Well, the goal missing from this example is stop. It strictly isn't required. I just use it to stop any stray process that has been running since before. But, I do use it, just in case. But you should not have start. I use run-forked instead of that, to ensure that a new JVM is started which uses the JaCoCo-agent. It is what makes code coverage work. Any of the examples work. I use something very close to the GitHub-example all the time now. Just keep asking if there are things I can help you with. – Daniel Lundmark Dec 19 '15 at 12:08
  • I'm still fighting with it. I now can run Jetty in forked mode and it starts fine, the tests are executed, but the results are zero percent coverage. I published my issue at http://stackoverflow.com/questions/34343214/how-to-have-the-maven-build-wait-for-the-jetty-server-to-start-in-forked-mode-be If you can help it'd be great, I'm quite stuck here. – Stephane Dec 21 '15 at 11:54