jacoco统计代码覆盖率的文章一搜一大堆,方法也很简单,就是在pom中引用两个插件:
maven-surefire-plugin
jacoco-maven-plugin
其中jacoco-maven-plugin的关键配置为要有两个execution:
一个goal是prepare-agent,即准备agent,实现为代码插桩
一个goal是report,顾名思义,即生成覆盖率报告
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
<configuration>
<skipTests>false</skipTests>
<testFailureIgnore>true</testFailureIgnore>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.5</version>
<executions>
<execution>
<id>my-prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>my-report</id>
<phase>test</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
但是如果是多模块的工程呢?如下面的结构:
├── module1
│ └── pom.xml
├── module2
│ └── pom.xml
├── module3
│ └── pom.xml
├── pom.xml
每个模块都这么配置的话,生成的报告是各自独立的,即会生成3个报告,那么怎么把各个模块的代码覆盖率统计在一起,生成一个聚合的报告呢?
可以参考七个披萨的博客,我觉得他的这篇文章写的特别好,强烈推荐读一下:jacoco插件配置生成单元测试覆盖率报告_七个披萨的博客-CSDN博客_jacoco:report-aggregate
我在这里复述一下其给出的做法:
jacoco提供了报告聚合的能力(goal为report-aggregate,注意与上面提到的report的区别),这就需要有一个模块来做报告聚合,这个模块需要引用所有的其他模块(以上面举例的工程为例,这个模块可以是新增的module4,也可以在module1/2/3中任选一个,一般在多模块的工程中,都会有一个主模块做为启动模块,可以用这个启动模块来做jacoco的报聚合)
划一下重点:
- 一个模块配置jacoco的report-aggregate
- 这个模块需要引用所有的其他模块
具体做法如下:
第一:在项目根目录下的父pom中添加jacoco-maven-plugin,并配置goal是prepare-agent(这么做的好处是不用在每个module的pom中都重复添加同样的内容)
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
<configuration>
<skipTests>false</skipTests>
<testFailureIgnore>true</testFailureIgnore>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.5</version>
<executions>
<execution>
<id>my-prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
第二:在聚合模块中配置jacoco聚合报告:
<plugins>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.5</version>
<executions>
<execution>
<id>my-report</id>
<phase>test</phase>
<goals>
<goal>report-aggregate</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
第三:在聚合模块中引用其他模块(非常重要,只有引用的模块的覆盖率才会被聚合到报告中,未引用的模块的覆盖率不会被收集和聚合):
<dependencies>
<dependency>
<groupId>@project.groupId@</groupId>
<artifactId>module1</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>@project.groupId@</groupId>
<artifactId>module2</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>@project.groupId@</groupId>
<artifactId>module3</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
但是我按照上述文章提到的方法配置后,可以得到聚合的报告了,但是覆盖率都为0,这又是怎么回事呢?
搜索了很多文章都无解,最后查看jacoco的help才找到答案:
需要在maven-surefire-plugin中配置引用jacoco的输出属性argLine
jacoco在prepare-agent阶段会生成一个属性指向jacoco的runtime agent,默认这个属性叫argLine,我们需要在maven-surefire-plugin的配置中,引用这个属性(当然我们也可以自定义这个属性的名字,那么就需要在maven-surefire-plugin的配置中引用我们自定义的属性名)
<configuration>
<argLine>${argLine}</argLine>
</configuration>
最终在根目录的pom.xml是这样的
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
<configuration>
<skipTests>false</skipTests>
<testFailureIgnore>true</testFailureIgnore>
<argLine>${argLine}</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.5</version>
<executions>
<execution>
<id>my-prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
或者在prepare-agent阶段自定义输出的属性名:
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.18.1</version>
<configuration>
<skipTests>false</skipTests>
<testFailureIgnore>true</testFailureIgnore>
<argLine>${jacocoArgLine}</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.5</version>
<executions>
<execution>
<id>my-prepare-agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
<configuration>
<propertyName>jacocoArgLine</propertyName>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
好了,这时再去运行一下:
mvn test -Dmaven.test.failure.ignore=true
看看聚合报告的结果吧。
jacoco帮助命令行提示原文如下,有兴趣的英文好的同学可以看看
jacoco:prepare-agent
Prepares a property pointing to the JaCoCo runtime agent that can be passed as
a VM argument to the application under test. Depending on the project
packaging type by default a property with the following name is set:
- tycho.testArgLine for packaging type eclipse-test-plugin and
- argLine otherwise.
If your project already defines VM arguments for test execution, be sure that
they will include property defined by JaCoCo.
One of the ways to do this in case of maven-surefire-plugin - is to use syntax
for late property evaluation:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>@{argLine} -your -extra -arguments</argLine>
</configuration>
</plugin>
You can define empty property to avoid JVM startup error Could not find or
load main class @{argLine} when using late property evaluation and
jacoco-maven-plugin not executed.
Another way is to define 'argLine' as a Maven property rather than as part of
the configuration of maven-surefire-plugin:
<properties>
<argLine>-your -extra -arguments</argLine>
</properties>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<!-- no argLine here -->
</configuration>
</plugin>
Resulting coverage information is collected during execution and by default
written to a file when the process terminates.