如何configuration多模块Maven + Sonar + JaCoCo提供合并报道报告?
我已经search了这个互联网上下。 有很多半的答案,与Maven属性(如${sonar.jacoco.reportPath}
或org.jacoco:jacoco-maven-plugin:prepare-agent
或设置maven-surefire-plugin
argLine
maven-surefire-plugin
argLine
和-javaagent
。
一些如何,这些答案,无论是自己,还是组合,正在产生我所追求的:一个覆盖报告,显示一个类,如果它被用于堆栈中较高的testing,如正在使用的实体通过DAO,即使它没有被自己的模块中的testing完全覆盖。
有没有一个确定的configuration,为了达到这个目的?
我和你的情况一样,分散在互联网上的一半的答案很烦人,因为似乎很多人都有同样的问题,但没有人能够完全解释他们是如何解决这个问题的。
Sonar文档提到了一个GitHub项目,其中有一些有用的例子 。 我所做的解决这个问题的方法是将集成testing逻辑应用于常规unit testing(尽pipe适当的unit testing应该是子模块特定的,但情况并非总是如此)。
在父pom.xml中,添加这些属性:
<properties> <!-- Sonar --> <sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin> <sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis> <sonar.jacoco.reportPath>${project.basedir}/../target/jacoco.exec</sonar.jacoco.reportPath> <sonar.language>java</sonar.language> </properties>
这将使Sonar为同一地点的所有子模块(父项目中的目标文件夹)提取unit testing报告。 它还告诉Sonar重新使用手动运行报告,而不是自行滚动报告。 我们只需要将jacoco-maven-plugin放在父/母内部的build / plugins中就可以运行所有的子模块:
<plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.6.0.201210061924</version> <configuration> <destFile>${sonar.jacoco.reportPath}</destFile> <append>true</append> </configuration> <executions> <execution> <id>agent</id> <goals> <goal>prepare-agent</goal> </goals> </execution> </executions> </plugin>
destFile
将报告文件放置在Sonar将要查找的位置,然后追加到文件中,而不是覆盖文件。 这将结合在同一个文件中的所有子模块的所有JaCoCo报告。
Sonar将为每个子模块查看该文件,因为这正是我们上面指出的,因此我们将在Sonar中为多模块文件提供组合testing结果。
常问问题
自那时以来,我一直在头脑中提出一些问题,让我对jacoco疯狂起来。
我的应用服务器(jBoss,Glassfish ..)位于伊拉克,叙利亚,无论..在运行集成testing时可以获得多模块覆盖吗? Jenkins和Sonar也在不同的服务器上。
是。 你必须使用以output=tcpserver
,jacoco ant lib模式运行的jacoco代理 。 基本上是两个jar
。 这会给你99%的成功。
jacoco代理如何工作?
你追加一个string
-javaagent:[your_path]/jacocoagent.jar=destfile=/jacoco.exec,output=tcpserver,address=*
到您的应用程序服务器JAVA_OPTS并重新启动它。 在这个string中,只有[your_path]
必须replace为jacocoagent.jar的path,存储(存储它!)在您的运行应用程序服务器的虚拟机上。 从那时起,您启动应用程序服务器,所有部署的应用程序将被dynamic监视,并且它们的活动(意味着代码使用情况)将准备好通过tcl请求获取jacocos .exec格式。
我可以重置jacoco代理来开始收集执行数据,因为我的testing开始的时间?
是的,为此,您需要位于jenkins工作区中的jacocoant.jar和ant构build脚本。
所以基本上我需要从http://www.eclemma.org/jacoco/是jacocoant.jar位于我的jenkins工作区,和jacocoagent.jar位于我的应用程序服务器VM?;
那就对了。
我不想用ant,我听说jacoco maven插件可以做所有的事情。
这是不对的,jacoco maven插件可以收集unit testing数据和一些集成testing数据(请参阅Arquillian Jacoco ),但是如果您有例如放心的testing作为jenkins中的单独构build,并希望显示多模块覆盖,我可以看看maven插件如何帮助你。
jacoco代理产生了什么?
只有.exec
格式的覆盖数据。 声纳然后可以读取它。
jacoco是否需要知道我的java类位于哪里?
不,声纳是,但不是雅可克。 当你做mvn sonar:sonar
道路就会发挥作用。
那么ant脚本呢?
它必须在您的jenkins工作区中呈现。 我的ant脚本,我叫它jacoco.xml
看起来像这样:
<project name="Jacoco library to collect code coverage remotely" xmlns:jacoco="antlib:org.jacoco.ant"> <property name="jacoco.port" value="6300"/> <property name="jacocoReportFile" location="${workspace}/it-jacoco.exec"/> <taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml"> <classpath path="${workspace}/tools/jacoco/jacocoant.jar"/> </taskdef> <target name="jacocoReport"> <jacoco:dump address="${jacoco.host}" port="${jacoco.port}" dump="true" reset="true" destfile="${jacocoReportFile}" append="false"/> </target> <target name="jacocoReset"> <jacoco:dump address="${jacoco.host}" port="${jacoco.port}" reset="true" destfile="${jacocoReportFile}" append="false"/> <delete file="${jacocoReportFile}"/> </target> </project>
当调用这个脚本时,你应该传递两个必须的参数-Dworkspace=$WORKSPACE
使用它来指向你的jenkins工作区和-Djacoco.host=yourappserver.com
主机没有http://
另外请注意,我把我的jacocoant.jar
放到$ {workspace} /tools/jacoco/jacocoant.jar
接下来我应该做什么?
你用jacocoagent.jar启动你的应用程序服务器吗?
你有没有把ant脚本和jacocoant.jar放在你的jenkins工作区中?
如果是,最后一步是configuration一个jenkins版本。 这是战略:
- 调用ant target
jacocoReset
重置所有以前收集的数据。 - 运行你的testing
- 调用ant target
jacocoReport
获取报告
如果一切正常,您将在构build工作区中看到it-jacoco.exec
。
看截图,我也在$WORKSPACE/tools/ant
目录中的工作区中安装了$WORKSPACE/tools/ant
,但是可以使用安装在jenkins中的安装目录。
如何在声纳中推送这个报告?
Maven sonar:sonar
会做这个工作(不要忘记configuration它),将它指向主要的pom.xml文件,这样它就可以运行所有的模块。 使用sonar.jacoco.itReportPath=$WORKSPACE/it-jacoco.exec
参数来告诉声纳集成testing报告的位置。 每次它将分析新的模块类,它将查找有关覆盖的信息it-jacoco.exec
。
我的`target`目录中已经有了jacoco.exec,`mvn sonar:sonar`忽略/删除它
默认情况下, mvn sonar:sonar
clean
,删除你的目标目录,使用sonar.dynamicAnalysis=reuseReports
来避免它。
从版本0.7.7开始的新方法
从版本0.7.7开始,有一种新的方法来创build汇总报告:
您创build一个单独的“报告”项目,收集所有必要的报告(聚合器项目中的任何目标在模块之前执行,因此无法使用)。
aggregator pom |- parent pom |- module a |- module b |- report module
根pom看起来像这样(不要忘记在模块下添加新的报告模块):
<build> <plugins> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.7.8</version> <executions> <execution> <id>prepare-agent</id> <goals> <goal>prepare-agent</goal> </goals> </execution> </executions> </plugin> </plugins>
来自每个子模块的poms根本不需要改变。 报告模块中的pom如下所示:
<!-- Add all sub modules as dependencies here --> <dependencies> <dependency> <module a> </dependency> <dependency> <module b> </dependency> ...
<build> <plugins> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.7.8</version> <executions> <execution> <id>report-aggregate</id> <phase>verify</phase> <goals> <goal>report-aggregate</goal> </goals> </execution> </executions> </plugin> </plugins> </build>
一个完整的例子可以在这里find。
我会发布我的解决scheme,因为它与其他人略有不同,并且在现有答案的帮助下花了我一天的时间。
对于一个多模块的Maven项目:
ROOT |--WAR |--LIB-1 |--LIB-2 |--TEST
WAR
项目是主要的Web应用程序, LIB
1和2是WAR
依赖的附加模块, TEST
是集成testing所在的地方。 TEST
join一个embedded式Tomcat实例(不是通过Tomcat插件),运行WAR
项目并通过一组JUnittesting来testing它们。 WAR
和LIB
项目都有自己的unit testing。
所有这些的结果是在SonarQube中整合和unit testing覆盖被分开并且能够被区分。
ROOT pom.xml
<!-- Sonar properties--> <sonar.jacoco.itReportPath>${project.basedir}/../target/jacoco-it.exec</sonar.jacoco.itReportPath> <sonar.jacoco.reportPath>${project.basedir}/../target/jacoco.exec</sonar.jacoco.reportPath> <sonar.language>java</sonar.language> <sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin> <!-- build/plugins (not build/pluginManagement/plugins!) --> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.7.6.201602180812</version> <executions> <execution> <id>agent-for-ut</id> <goals> <goal>prepare-agent</goal> </goals> <configuration> <append>true</append> <destFile>${sonar.jacoco.reportPath}</destFile> </configuration> </execution> <execution> <id>agent-for-it</id> <goals> <goal>prepare-agent-integration</goal> </goals> <configuration> <append>true</append> <destFile>${sonar.jacoco.itReportPath}</destFile> </configuration> </execution> </executions> </plugin>
WAR
, LIB
和TEST
pom.xml
将inheritanceJaCoCo插件的执行。
TEST pom.xml
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> <version>2.19.1</version> <executions> <execution> <goals> <goal>integration-test</goal> <goal>verify</goal> </goals> <configuration> <skipTests>${skip.tests}</skipTests> <argLine>${argLine} -Duser.timezone=UTC -Xms256m -Xmx256m</argLine> <includes> <includes>**/*Test*</includes> </includes> </configuration> </execution> </executions> </plugin>
我还发现Petri Kainulainens博客文章“为JaCoCo Maven插件创build单元和集成testing的代码覆盖率报告”对于JaCoCo安装方面是有价值的。
我在父级别的configuration中使用了configuration,我有独立的单元和集成testing阶段。
我在父POM属性中configuration了以下属性
<maven.surefire.report.plugin>2.19.1</maven.surefire.report.plugin> <jacoco.plugin.version>0.7.6.201602180812</jacoco.plugin.version> <jacoco.check.lineRatio>0.52</jacoco.check.lineRatio> <jacoco.check.branchRatio>0.40</jacoco.check.branchRatio> <jacoco.check.complexityMax>15</jacoco.check.complexityMax> <jacoco.skip>false</jacoco.skip> <jacoco.excludePattern/> <jacoco.destfile>${project.basedir}/../target/coverage-reports/jacoco.exec</jacoco.destfile> <sonar.language>java</sonar.language> <sonar.exclusions>**/generated-sources/**/*</sonar.exclusions> <sonar.core.codeCoveragePlugin>jacoco</sonar.core.codeCoveragePlugin> <sonar.coverage.exclusions>${jacoco.excludePattern}</sonar.coverage.exclusions> <sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis> <sonar.jacoco.reportPath>${project.basedir}/../target/coverage-reports</sonar.jacoco.reportPath> <skip.surefire.tests>${skipTests}</skip.surefire.tests> <skip.failsafe.tests>${skipTests}</skip.failsafe.tests>
我把插件定义在插件pipe理下。
请注意,我为surefire(surefireArgLine)和failsafe(failsafeArgLine)参数定义了一个属性,以允许jacoco将javaagentconfiguration为在每个testing中运行。
在pluginManagement下
<build> <pluginManagment> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <fork>true</fork> <meminitial>1024m</meminitial> <maxmem>1024m</maxmem> <compilerArgument>-g</compilerArgument> <source>${maven.compiler.source}</source> <target>${maven.compiler.target}</target> <encoding>${project.build.sourceEncoding}</encoding> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.19.1</version> <configuration> <forkCount>4</forkCount> <reuseForks>false</reuseForks> <argLine>-Xmx2048m ${surefireArgLine}</argLine> <includes> <include>**/*Test.java</include> </includes> <excludes> <exclude>**/*IT.java</exclude> </excludes> <skip>${skip.surefire.tests}</skip> </configuration> </plugin> <plugin> <!-- For integration test separation --> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> <version>2.19.1</version> <dependencies> <dependency> <groupId>org.apache.maven.surefire</groupId> <artifactId>surefire-junit47</artifactId> <version>2.19.1</version> </dependency> </dependencies> <configuration> <forkCount>4</forkCount> <reuseForks>false</reuseForks> <argLine>${failsafeArgLine}</argLine> <includes> <include>**/*IT.java</include> </includes> <skip>${skip.failsafe.tests}</skip> </configuration> <executions> <execution> <id>integration-test</id> <goals> <goal>integration-test</goal> </goals> </execution> <execution> <id>verify</id> <goals> <goal>verify</goal> </goals> </execution> </executions> </plugin> <plugin> <!-- Code Coverage --> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>${jacoco.plugin.version}</version> <configuration> <haltOnFailure>true</haltOnFailure> <excludes> <exclude>**/*.mar</exclude> <exclude>${jacoco.excludePattern}</exclude> </excludes> <rules> <rule> <element>BUNDLE</element> <limits> <limit> <counter>LINE</counter> <value>COVEREDRATIO</value> <minimum>${jacoco.check.lineRatio}</minimum> </limit> <limit> <counter>BRANCH</counter> <value>COVEREDRATIO</value> <minimum>${jacoco.check.branchRatio}</minimum> </limit> </limits> </rule> <rule> <element>METHOD</element> <limits> <limit> <counter>COMPLEXITY</counter> <value>TOTALCOUNT</value> <maximum>${jacoco.check.complexityMax}</maximum> </limit> </limits> </rule> </rules> </configuration> <executions> <execution> <id>pre-unit-test</id> <goals> <goal>prepare-agent</goal> </goals> <configuration> <destFile>${jacoco.destfile}</destFile> <append>true</append> <propertyName>surefireArgLine</propertyName> </configuration> </execution> <execution> <id>post-unit-test</id> <phase>test</phase> <goals> <goal>report</goal> </goals> <configuration> <dataFile>${jacoco.destfile}</dataFile> <outputDirectory>${sonar.jacoco.reportPath}</outputDirectory> <skip>${skip.surefire.tests}</skip> </configuration> </execution> <execution> <id>pre-integration-test</id> <phase>pre-integration-test</phase> <goals> <goal>prepare-agent-integration</goal> </goals> <configuration> <destFile>${jacoco.destfile}</destFile> <append>true</append> <propertyName>failsafeArgLine</propertyName> </configuration> </execution> <execution> <id>post-integration-test</id> <phase>post-integration-test</phase> <goals> <goal>report-integration</goal> </goals> <configuration> <dataFile>${jacoco.destfile}</dataFile> <outputDirectory>${sonar.jacoco.reportPath}</outputDirectory> <skip>${skip.failsafe.tests}</skip> </configuration> </execution> <!-- Disabled until such time as code quality stops this tripping <execution> <id>default-check</id> <goals> <goal>check</goal> </goals> <configuration> <dataFile>${jacoco.destfile}</dataFile> </configuration> </execution> --> </executions> </plugin> ...
并在构build部分
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> </plugin> <plugin> <!-- for unit test execution --> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> </plugin> <plugin> <!-- For integration test separation --> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> </plugin> <plugin> <!-- For code coverage --> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> </plugin> ....
并在报告部分
<reporting> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-report-plugin</artifactId> <version>${maven.surefire.report.plugin}</version> <configuration> <showSuccess>false</showSuccess> <alwaysGenerateFailsafeReport>true</alwaysGenerateFailsafeReport> <alwaysGenerateSurefireReport>true</alwaysGenerateSurefireReport> <aggregate>true</aggregate> </configuration> </plugin> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>${jacoco.plugin.version}</version> <configuration> <excludes> <exclude>**/*.mar</exclude> <exclude>${jacoco.excludePattern}</exclude> </excludes> </configuration> </plugin> </plugins> </reporting>
有一种方法可以做到这一点。 神奇的是创build一个组合的jacoco.exec文件。 而使用maven 3.3.1有一个简单的方法来获得这个。 我的个人资料:
<profile> <id>runSonar</id> <activation> <property> <name>runSonar</name> <value>true</value> </property> </activation> <properties> <sonar.language>java</sonar.language> <sonar.host.url>http://sonar.url</sonar.host.url> <sonar.login>tokenX</sonar.login> <sonar.jacoco.reportMissing.force.zero>true</sonar.jacoco.reportMissing.force.zero> <sonar.jacoco.reportPath>${jacoco.destFile}</sonar.jacoco.reportPath> <jacoco.destFile>${maven.multiModuleProjectDirectory}/target/jacoco_analysis/jacoco.exec</jacoco.destFile> </properties> <build> <plugins> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <executions> <execution> <id>default-prepare-agent</id> <goals> <goal>prepare-agent</goal> </goals> <configuration> <append>true</append> <destFile>${jacoco.destFile}</destFile> </configuration> </execution> </executions> </plugin> </plugins> <pluginManagement> <plugins> <plugin> <groupId>org.sonarsource.scanner.maven</groupId> <artifactId>sonar-maven-plugin</artifactId> <version>3.2</version> </plugin> <plugin> <groupId>org.jacoco</groupId> <artifactId>jacoco-maven-plugin</artifactId> <version>0.7.8</version> </plugin> </plugins> </pluginManagement> </build> </profile>
如果你添加这个configuration文件到你的父母pom,并调用mvn clean install sonar:sonar -DrunSonar
你得到完整的覆盖。
这里的魔法是maven.multiModuleProjectDirectory
。 这个文件夹永远是你开始构buildMaven的文件夹。
你可以在maven中调用一个叫做merge的ant任务,把所有的覆盖文件(* .exec)放在同一个文件中。
如果你正在运行unit testing,使用阶段prepare-package ,如果你运行集成testing,那么使用post-integration-test 。
这个网站有一个如何在maven项目中调用jacoco ant任务的例子
你可以在声纳上使用这个合并的文件。
要进行unit testing和集成testing,您可以使用maven-surefire-plugin和maven-failsafe-plugin包含受限制的包含/排除。 我正在和CDI玩,同时与sonar / jacoco联系,所以我结束了这个项目:
https://github.com/FibreFoX/cdi-sessionscoped-login/
也许它可以帮助你一点点。 在我的pom.xml中,通过在指定的testing插件的configuration部分中设置argLine-option,我使用了“-javaagent”。 在MAVEN项目中明确使用ANT是我不会试一试的,对我来说,要混合两个世界。
我只有一个单一的模块maven项目,但也许它可以帮助你调整你的工作。
注意:也许不是所有的maven-plugins都是up2date的,也许有些问题在以后的版本中修复