Tomcat7 Maven 插件和 JaCoCo
Posted
技术标签:
【中文标题】Tomcat7 Maven 插件和 JaCoCo【英文标题】:Tomcat7 Maven Plugin and JaCoCo 【发布时间】:2013-04-07 18:36:08 【问题描述】:是否有任何方法可以使用 JaCoCo 和 tomcat7-maven-plugin 嵌入式实例来获得代码覆盖率?
在我的 WAR 的 POM 中配置了 jacoco-maven-plugin 以检测我的单元测试,但我不确定如何将 jacoco 代理附加到嵌入式 Tomcat 实例以检测针对 Tomcat 运行的集成测试。鉴于 Tomcat 实例是嵌入式的,我不确定这种方法是否可行。有没有其他方法可以做到这一点?我可能可以从使用 Tomcat Maven 插件切换到使用 Cargo 来获得覆盖,但如果可能的话,我更愿意坚持使用 Tomcat 插件。
以下是我 POM 中的一些相关 sn-ps:
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.6.2.201302030002</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.14</version>
<executions>
<execution>
<id>integration-tests</id>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<configuration>
<systemProperties>
<!-- as expected, this system property doesn't work since Tomcat is embedded, but this is the type of config I'm looking for -->
<JAVA_OPTS>-javaagent:$project.build.directory/$jacoco.jar=destfile=$project.build.directory/jacoco.exec,append=true</JAVA_OPTS>
</systemProperties>
</configuration>
<executions>
<execution>
<id>tomcat-startup</id>
<goals>
<goal>run-war-only</goal>
</goals>
<phase>pre-integration-test</phase>
<configuration>
<fork>true</fork>
</configuration>
</execution>
<execution>
<id>tomcat-shutdown</id>
<goals>
<goal>shutdown</goal>
</goals>
<phase>post-integration-test</phase>
</execution>
</executions>
</plugin>
版本:Maven 3.0.4、Tomcat Maven 插件 2.1、Jacoco 0.6.2.201302030002、Java 7
【问题讨论】:
刚刚偶然发现MTOMCAT-83 似乎描述了同样的问题。 【参考方案1】:我设法做到了,这涉及到一些挑剔的东西:
服务器/容器需要位于可以接收参数的单独 jvm (jacoco-agent) 上。使用嵌入式容器的货物似乎不起作用,调试起来很痛苦...... jvm 与 jacoco-it 需要在 jacoco 分析之前停止 (duh!) 但在 post-integration-test 上注册 container-stop 和 jacoco-report 并不能保证这一点...(前面的 tcpdump 等选项答案有这个问题) 为服务器/容器定义随机端口可以轻松与持续集成进行集成 phantomjs 是额外的 ;) jacoco 应该用作集成测试的准备代理集成和报告集成(实际上并没有什么区别)应该作为'mvn clean verify'运行
Pom:
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.4.201502262128</version>
<executions>
<!-- unit test coverage -->
<execution>
<id>jacoco-pre-unit-test</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<destFile>$project.build.directory/coverage-reports/jacoco-ut.exec</destFile>
<propertyName>jacoco.ut.argLine</propertyName>
</configuration>
</execution>
<execution>
<id>jacoco-post-unit-test</id>
<phase>test</phase>
<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>
<!-- integration test coverage -->
<execution>
<id>jacoco-pre-integration-test</id>
<phase>pre-integration-test</phase>
<goals>
<goal>prepare-agent-integration</goal>
</goals>
<configuration>
<destFile>$project.build.directory/coverage-reports/jacoco-it.exec</destFile>
<propertyName>jacoco.it.argLine</propertyName>
</configuration>
</execution>
<execution>
<id>jacoco-post-integration-test</id>
<phase>post-integration-test</phase>
<goals>
<goal>report-integration</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.codehaus.mojo</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>2.6</version>
</plugin>
<!-- Installs PhantomJS so it doesn't have to be pre-installed -->
<plugin>
<groupId>com.github.klieber</groupId>
<artifactId>phantomjs-maven-plugin</artifactId>
<version>0.4</version>
<executions>
<execution>
<!-- should be post-integration-test ? -->
<phase>test</phase>
<goals>
<goal>install</goal>
</goals>
</execution>
</executions>
<configuration>
<version>1.9.7</version>
</configuration>
</plugin>
<!-- Get two free ports for our test server to use -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.6</version>
<configuration>
<portNames>
<portName>jetty.port</portName>
<portName>jetty.port.stop</portName>
</portNames>
</configuration>
<executions>
<execution>
<id>reserve-port</id>
<phase>test</phase>
<goals>
<goal>reserve-network-port</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Run tests (UT) -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
<configuration>
<argLine>$jacoco.ut.argLine</argLine>
<skipTests>$skip.unit.tests</skipTests>
<testFailureIgnore>true</testFailureIgnore>
<excludes>
<!-- no UT execution, to test only IT
<exclude>**/<remove this>*Test.java</exclude> -->
</excludes>
</configuration>
</plugin>
<!-- Use failsafe to run our integration tests -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.17</version>
<configuration>
<systemPropertyVariables>
<!-- pass these values to the test classes -->
<phantomjs.binary>$phantomjs.binary</phantomjs.binary>
<jetty.port>$jetty.port</jetty.port>
</systemPropertyVariables>
</configuration>
<executions>
<execution>
<id>integration-test</id>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<version>1.4.16</version>
<configuration>
<skip>$skipITs</skip>
<container>
<containerId>tomcat8x</containerId>
<zipUrlInstaller>
<!-- did not work with 'https'. Certificate problem? -->
<url>http://archive.apache.org/dist/tomcat/tomcat-8/v8.0.26/bin/apache-tomcat-8.0.26.zip</url>
<downloadDir>$project.build.directory/downloads</downloadDir>
<extractDir>$project.build.directory/extracts</extractDir>
</zipUrlInstaller>
</container>
<configuration>
<home>$project.build.directory/catalina-base</home>
<properties>
<cargo.jvmargs>$jacoco.it.argLine</cargo.jvmargs>
<cargo.servlet.port>$jetty.port</cargo.servlet.port>
<!-- <cargo.logging>high</cargo.logging> -->
</properties>
</configuration>
</configuration>
<executions>
<execution>
<id>cargo-start-tomcat</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
</goals>
</execution>
<execution>
<id>cargo-stop-tomcat</id>
<phase>integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
【讨论】:
【参考方案2】:我遇到了完全相同的问题,我找到的唯一解决方案是将 MAVEN_OPTS
之前设置为 Maven 构建(例如在命令行或 Jenkins 作业的配置中):
export MAVEN_OPTS=-javaagent:~/.m2/repository/org/jacoco/org.jacoco.agent/0.7.4.201502262128/org.jacoco.agent-0.7.4.201502262128-runtime.jar=destfile=./target/jacoco.exec,append=true
这会将 jacoco 代理附加到嵌入式 tomcat 实例,该实例会将覆盖结果报告给给定的destfile
。
首先,重要的是 jacoco 运行时 JAR 的路径正确。您可以手动下载并引用它或使用其他 Maven 命令将其下载到本地 .m2 存储库中:
mvn org.apache.maven.plugins:maven-dependency-plugin:2.8:get -Dartifact=org.jacoco:org.jacoco.agent:0.7.4.201502262128:jar:runtime
其次,确保jacoco.exec
文件的路径正确。就我而言,我已经在目标文件夹中有一个现有的 jacoco.exec 文件,其中包含单元测试结果。 append=true
确保单元测试和集成测试相结合。
【讨论】:
不推荐使用 jacoco 代理运行整个 maven 进程。而且您需要手动指定版本(并直接引用您的 maven 存储库),而不仅仅是使用插件...【参考方案3】:我知道问题发布已经有一段时间了,但我觉得答案并没有真正解决问题的根源。如果您在这些插件中运行测试,代码覆盖率可能适用于故障安全或万无一失。但是,如果您只想使用 jacoco 监视 tomcat 以获取覆盖率报告,则当前信息无法提供。我发现tomcat7-maven-plugin 不允许您通过简单地提供JAVA_OPTS 来为jacoco 注入-javaagent。切换到货物我可以这样做。
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.2.201409121644</version>
<configuration>
<destFile>$sonar.jacoco.reportPath</destFile>
<dataFile>$sonar.jacoco.reportPath</dataFile>
<outputDirectory>$project.reporting.outputDirectory/jacoco-it</outputDirectory>
<classDumpDir>$project.reporting.outputDirectory/jacoco-it/classes</classDumpDir>
<skip>$skipITs</skip>
</configuration>
<executions>
<execution>
<id>jacoco-agent</id>
<phase>pre-integration-test</phase>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<destFile>$sonar.jacoco.reportPath</destFile>
<propertyName>jacoco.agent.itArgLine</propertyName>
</configuration>
</execution>
<execution>
<id>jacoco-report</id>
<phase>post-integration-test</phase>
<goals>
<goal>dump</goal>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<version>1.4.11</version>
<configuration>
<skip>$skipITs</skip>
<container>
<containerId>tomcat7x</containerId>
<zipUrlInstaller>
<url>http://archive.apache.org/dist/tomcat/tomcat-7/v7.0.16/bin/apache-tomcat-7.0.16.zip
</url>
<downloadDir>$project.build.directory/downloads</downloadDir>
<extractDir>$project.build.directory/extracts</extractDir>
</zipUrlInstaller>
<dependencies>
<dependency>
<groupId>ojdbc</groupId>
<artifactId>ojdbc6</artifactId>
</dependency>
</dependencies>
</container>
<configuration>
<home>$project.build.directory/catalina-base</home>
<properties>
<cargo.jvmargs>$jacoco.agent.itArgLine,output=tcpserver,port=6300 -Drunmode=TEST</cargo.jvmargs>
<cargo.servlet.port>9090</cargo.servlet.port>
</properties>
<configfiles>
<configfile>
<file>$basedir/src/test/conf/context.xml</file>
<todir>conf/Catalina/localhost/</todir>
<tofile>context.xml.default</tofile>
</configfile>
</configfiles>
</configuration>
</configuration>
<executions>
<execution>
<id>start-tomcat</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
</goals>
</execution>
<execution>
<id>stop-tomcat</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
重要的部分是:
<plugin><groupId>org.jacoco</groupId> ...<propertyName>jacoco.agent.itArgLine</propertyName>
<cargo.jvmargs>$jacoco.agent.itArgLine,output=tcpserver,port=6300 </cargo.jvmargs>
当在 jacoco 插件上运行报告目标时,它将在 $projectbase/target/site/jacoco-it/index.html 中创建一个包含您的覆盖率报告的目录。我将它与 soapui-maven-plugin 一起使用,但它也可以与 selenium-maven-plugin 一起使用。
【讨论】:
感谢您的回复。正如我最初的问题中提到的,我知道我可以使用 cargo 来完成这个(这是我的临时解决方法),但我想知道如何使用 tomcat7-maven-plugin 来完成这个。 @shelley 您无法使用 tomcat7-maven-plugin 附加 jacoco 代理。在这方面,您应该向该项目提出问题。大约 2 年前,当我为我的项目执行此操作时,我不得不使用 jetty 插件。现在这不适用于 Java 8。看起来我将考虑遵循这个答案中的方法。【参考方案4】:我解决了在使用嵌入式 Tomcat 设置 JaCoCo 代理时遇到的问题,方法是离线检测类,然后将 JaCoCo 代理放在 Tomcat 类路径(插件依赖项)上并添加文件 jacoco-agent.properties。
我把工作配置放在我的博客上:
http://burkond.blogspot.de/2014/05/selenium-in-sonar-code-coverage-metrics.html
【讨论】:
【参考方案5】:如果您使用 maven-failsafe-plugin(或 maven-surefire-plugin),则无需将 JAVA_OPTS 传递给 tomcat Embedded运行您的集成测试。这是因为tomcat Embedded运行在maven-failsafe-plugin的同一个进程中。
所以当 jacoco-maven-plugin 执行 prepare-agent 时,它会将 argLine 设置为 maven-failsafe-plugin uses too。
我创建了一个项目来测试这个,在 pom 的下面部分:
<build>
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.6.2.201302030002</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<includes>
<include>my.project.package.only.*</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.1</version>
<executions>
<execution>
<id>tomcat-startup</id>
<goals>
<goal>run-war-only</goal>
</goals>
<phase>pre-integration-test</phase>
<configuration>
<fork>true</fork>
</configuration>
</execution>
<execution>
<id>tomcat-shutdown</id>
<goals>
<goal>shutdown</goal>
</goals>
<phase>post-integration-test</phase>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<id>integration-tests</id>
<phase>integration-test</phase>
<goals>
<goal>integration-test</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>selenium-maven-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<id>start</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start-server</goal>
</goals>
<configuration>
<background>true</background>
<logOutput>true</logOutput>
<multiWindow>true</multiWindow>
</configuration>
</execution>
<execution>
<id>stop</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop-server</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
【讨论】:
上面的JAVA_OPTS配置是cargo还是tomcat插件?正如我在原始问题中提到的并在我的 POM 中说明的那样,我已经尝试为 Maven Tomcat 插件设置 JAVA_OPTS,但不相信它适用于嵌入式服务器,所以我不太确定这里的建议是什么。 我之前看过那个 SO 帖子并对其进行了测试,但它没有工作(正如预期的那样,每个 this post、this issue,并且考虑到它是嵌入的)。您是否能够像上面演示的那样使用 Maven Tomcat 插件实际运行 jacoco,还是只是建议我在最初的问题中发布的配置应该起作用? 代理甚至没有被启动。 Tomcat 插件按预期工作;我一直在用这种方式运行我的集成测试一段时间,但刚刚尝试将 jacoco 添加到它。 我也遇到了同样的问题!!!!我花了 10 多个小时调试这个。有什么决议吗?我正在使用 Gradle (1.5)、Embedded Tomcat (7.0.29)、Jacoco 插件 (0.6.2+)、Java 7,并且我明确地看到了正在设置的 javaagent 的 JVM 参数。我尝试使用“tcpserver”版本,但没有绑定到 6300 的端口(默认 jacoco 转储端口)。有什么想法吗? 嗨,我更改了答案,因为我做了一个适用于您的问题的示例。我也删除了旧的 cmets。【参考方案6】:您可以尝试在此帖子中查看解决方法:http://dougonjava.blogspot.co.il/2013/07/integration-testing-using-maven-tomcat.html
【讨论】:
以上是关于Tomcat7 Maven 插件和 JaCoCo的主要内容,如果未能解决你的问题,请参考以下文章
maven插件介绍之tomcat7-maven-plugin