You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多模块Spring Boot项目Jacoco-Sonar集成:Controller覆盖率未统计问题排查

解决Controller覆盖率统计及相关疑问

一、让Controller被Jacoco覆盖率统计的具体方案

你遇到的Controller未被集成测试(用MockMvc调用)统计覆盖率的问题,核心原因通常是Jacoco Agent没有在集成测试阶段正确拦截Spring MVC的请求调用链,或者单元测试与集成测试的覆盖率数据没被正确合并。结合你这种Spring Boot多模块的场景,按下面的步骤调整配置就能解决:

1. 确保集成测试阶段启动Jacoco Agent

在根pom.xml的<build>节点下配置jacoco-maven-plugin,为单元测试和集成测试分别配置Agent,收集各自的覆盖率数据:

<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.11</version>
    <executions>
        <!-- 收集单元测试覆盖率 -->
        <execution>
            <id>jacoco-ut</id>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
            <configuration>
                <destFile>${project.build.directory}/jacoco-ut.exec</destFile>
                <propertyName>jacoco.ut.agent.argLine</propertyName>
            </configuration>
        </execution>
        <!-- 收集集成测试覆盖率 -->
        <execution>
            <id>jacoco-it</id>
            <goals>
                <goal>prepare-agent-integration</goal>
            </goals>
            <configuration>
                <destFile>${project.build.directory}/jacoco-it.exec</destFile>
                <propertyName>jacoco.it.agent.argLine</propertyName>
            </configuration>
        </execution>
        <!-- 生成单元测试报告 -->
        <execution>
            <id>jacoco-ut-report</id>
            <phase>test</phase>
            <goals>
                <goal>report</goal>
            </goals>
            <configuration>
                <dataFile>${project.build.directory}/jacoco-ut.exec</dataFile>
                <outputDirectory>${project.reporting.outputDirectory}/jacoco-ut</outputDirectory>
            </configuration>
        </execution>
        <!-- 生成集成测试报告 -->
        <execution>
            <id>jacoco-it-report</id>
            <phase>post-integration-test</phase>
            <goals>
                <goal>report</goal>
            </goals>
            <configuration>
                <dataFile>${project.build.directory}/jacoco-it.exec</dataFile>
                <outputDirectory>${project.reporting.outputDirectory}/jacoco-it</outputDirectory>
            </configuration>
        </execution>
    </executions>
</plugin>

同时,要确保集成测试使用的maven-failsafe-plugin能用到Jacoco的Agent参数,不然集成测试的调用还是不会被记录:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-failsafe-plugin</artifactId>
    <version>3.2.5</version>
    <executions>
        <execution>
            <goals>
                <goal>integration-test</goal>
                <goal>verify</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <argLine>${jacoco.it.agent.argLine}</argLine>
    </configuration>
</plugin>

这样配置后,MockMvc发起的请求会通过Spring的DispatcherServlet转发到Controller实例,Jacoco Agent会记录下这些调用轨迹,Controller的覆盖率就会被统计到了。

2. 合并单元测试与集成测试的覆盖率数据

多模块场景下,需要把各模块的单元测试(jacoco-ut.exec)和集成测试(jacoco-it.exec)数据合并,才能在Sonar中看到完整的覆盖率。你可以创建一个单独的聚合模块(后面解释为什么不推荐在根模块做),在这个模块的pom.xml中配置:

<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.11</version>
    <executions>
        <execution>
            <id>jacoco-aggregate</id>
            <phase>verify</phase>
            <goals>
                <goal>report-aggregate</goal>
            </goals>
            <configuration>
                <dataFiles>
                    <!-- 替换成你的web模块路径 -->
                    <dataFile>${project.parent.basedir}/web/target/jacoco-ut.exec</dataFile>
                    <dataFile>${project.parent.basedir}/web/target/jacoco-it.exec</dataFile>
                    <!-- 替换成你的业务模块路径 -->
                    <dataFile>${project.parent.basedir}/service/target/jacoco-ut.exec</dataFile>
                    <!-- 其他模块的exec文件都加在这里 -->
                </dataFiles>
                <outputDirectory>${project.reporting.outputDirectory}/jacoco-aggregate</outputDirectory>
            </configuration>
        </execution>
    </executions>
</plugin>

最后在Sonar的配置中指定这个聚合报告的路径,让Sonar读取合并后的完整覆盖率数据:

<plugin>
    <groupId>org.sonarsource.scanner.maven</groupId>
    <artifactId>sonar-maven-plugin</artifactId>
    <version>3.10.0.2594</version>
    <configuration>
        <sonar.jacoco.reportPaths>${project.parent.basedir}/aggregate/target/site/jacoco-aggregate/jacoco.xml</sonar.jacoco.reportPaths>
    </configuration>
</plugin>

二、解答你的两个疑问

疑问1:为何必须单独创建模块来聚合结果,不能在根模块完成?

其实不是绝对必须,但在根模块做聚合会踩Maven生命周期的坑:

  • 根模块是pom类型,它的生命周期阶段(比如verify)不会自动等待所有子模块的test和integration-test阶段执行完毕。如果在根模块配置report-aggregate,很可能子模块的exec文件还没生成,根模块就已经执行聚合操作了,导致报告为空。
  • 单独的聚合模块(同样是pom类型)可以通过依赖所有业务子模块,确保子模块的所有测试任务先执行完,再进行覆盖率数据的聚合,避免时序问题。
  • 从维护角度看,单独的聚合模块把覆盖率聚合逻辑和业务代码解耦,后续调整起来更清晰,不会和业务模块的配置混在一起。

疑问2:Spring Boot项目用MockMvc做集成测试,Controller不是直接实例化调用,为什么会影响覆盖率?

MockMvc的工作逻辑是模拟Servlet容器环境,通过Spring的DispatcherServlet转发请求到容器管理的Controller Bean——这个Controller实例是Spring正常初始化的,Jacoco Agent会在类加载时给Controller的字节码织入统计逻辑,只要DispatcherServlet调用了Controller的方法,Jacoco就会记录下这些调用。

之前没统计到覆盖率,不是MockMvc的问题,而是:

  • 集成测试阶段没有启动Jacoco Agent,导致Controller的调用轨迹没被记录;
  • 集成测试生成的exec文件没被合并到最终的覆盖率报告中,Sonar没读到这部分数据。

内容的提问来源于stack exchange,提问作者slim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 18:57:44