Spring Boot中使用JaCoCo设置Maven多模块代码覆盖率

代码覆盖率是软件开发的一个重要方面,帮助我们确保我们的代码经过彻底的测试。JaCoCo(Java 代码覆盖率)是一种流行的工具,用于测量 Java 应用程序中的代码覆盖率。

本文将探讨如何在 Spring Boot 项目中使用 JaCoCo 设置 Maven 多模块代码覆盖率。

1.了解Maven多模块项目
Maven多模块项目就像一个由较小项目组成的大项目。主项目收集并组织这些较小的项目,使其更容易处理并保持整洁。这是一种以结构化和有组织的方式构建软件的方法,其中每个小部分在整个项目中都有其作用。这种结构有利于大规模应用程序,促进代码重用、可维护性和高效的项目管理。

2.设置 Maven 多模块项目
为 Spring Boot 应用程序的不同部分创建子模块。例如,让我们创建两个子模块:core和web:

mvn archetype:generate -DgroupId=com.jcg.core -DartifactId=core -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
 
mvn archetype:generate -DgroupId=com.jcg.web -DartifactId=web -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

打开父项目目录中的 pom.xml 文件并添加以下 <modules> 部分(如果不存在)。父 pom.xml 文件应如下所示:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
     
    <groupId>com.jcg</groupId>
    <artifactId>jacoco-parent-project</artifactId>
    <version>0.0.1-SNAPSHOT</version>        
    <name>jacoco-parent-project</name>
    <packaging>pom</packaging>
    <description>Demo project for Maven Multi-Module Project</description>
         
    <modules>
        <module>core</module>
        <module>web</module>
    </modules>
     
    <properties>
        <java.version>17</java.version>
        <spring.boot.version>3.2.0</spring.boot.version>
    </properties>
         
    <build>
       
        <plugins>
       
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.10.1</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
 
            <!-- Maven Surefire plugin for running tests -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.0.0-M5</version>
            </plugin>
         
        </plugins>
    </build>
   
</project>


创建core包含业务逻辑的模块。

package com.jcg.core;
 
public class Calculator {
  
    public int add(int a, int b) {
        return a + b;
    }
}

在本示例中,Calculator 类提供了一个简单的方法 add,该方法将两个整数作为参数,并返回它们的和。该类将用于web模块中的 CalculatorController,展示了多模块 Spring Boot 项目中的关注点分离。

我们将为核心模块中的计算器类创建一个测试类。

public class CalculatorTest {
 
    @Test
    public void testAdd() {
         Calculator calculator = new Calculator();
        int result = calculator.add(2, 3);
        assertEquals(5, result);
    }
  
}

确保覆盖核心模块 - Surefire 插件配置
通过在 pom.xml 中添加 maven-surefire-plugin 来确保核心模块已覆盖。这将使用 Maven Surefire 插件来执行单元测试。

<!-- core/pom.xml -->
<build>
<!-- Maven Surefire plugin for running unit test -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.1.2</version>
            </plugin>
         
        </plugins>
</build>

创建包含 Spring Boot 应用程序的web模块。

RestController
@RequestMapping("/calculator")
public class CalculatorController {
 
    private final Calculator calculator;
 
    @Autowired
    public CalculatorController(Calculator calculator) {
        this.calculator = calculator;
    }
     
    @GetMapping("/add")
    public int add(@RequestParam int a, @RequestParam int b) {
        return calculator.add(a, b);
    }
       
}

该控制器提供了一个简单的 RESTful 端点,用于添加两个数字。在此示例中

  • 使用 @RestController 表示该类是处理 HTTP 请求并返回 JSON 响应的控制器。
  • @RequestMapping("/calculator") 指定了该控制器中所有端点的基本路径。
  • 构造函数使用 @Autowired 进行注解,以便将计算器实例注入控制器。
  • 添加方法被映射到 /add 端点,并从请求参数中获取两个参数(a 和 b)。它将加法操作委托给注入的计算器实例,并返回结果。

让我们为web模块中的 CalculatorController 类创建一个测试类。

@WebMvcTest(CalculatorController.class)
public class CalculatorControllerTest {
 
    @Autowired
    private MockMvc mockMvc;
     
    @Mock
    private Calculator calculator;
 
    @Test
    public void testAdd() throws Exception {
         
        when(calculator.add(anyInt(), anyInt())).thenReturn(5);
         
        int a = 2;
        int b = 3;
 
        mockMvc.perform(get("/calculator/add")
                .param("a", String.valueOf(a))
                .param("b", String.valueOf(b)))
                .andExpect(status().isOk())
                .andExpect(content().string("5"));
    }
 
}

在本例中,我们使用 @WebMvcTest 来重点测试 CalculatorController 类。在这个测试中

  • @WebMvcTest(CalculatorController.class) 用于集中测试 CalculatorController 类。
  • 我们注入了一个 MockMvc 实例来执行 HTTP 请求并断言响应。
  • @Mock 用于模拟 Calculator 依赖关系。
  • testAdd 方法向 /calculator/add 端点执行 GET 请求,参数为 a=2 和 b=3。
  • 计算器添加方法的行为被模拟为返回 5。
  • 我们提出了断言,以确保响应状态为 OK,响应内容为 "5"。

该测试可确保 CalculatorController 与计算器正确交互并产生预期响应。

集成测试的 Maven Failsafe 插件
更新web模块 pom.xml,以便在构建过程中管理集成测试的执行。

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-failsafe-plugin</artifactId>
            <version>3.1.2</version>
            <executions>
                <execution>
                    <goals>
                        <goal>integration-test</goal>
                        <goal>verify</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

为多模块覆盖配置 JaCoCo
要在 Maven 项目中为多模块覆盖配置 JaCoCo 插件,我们需要在父项目的 pom.xml 中包含 JaCoCo 插件配置。这样,我们就能汇总来自多个模块的代码覆盖率报告。下面是一个示例,说明如何设置 JaCoCo 以测量多模块项目中的代码覆盖率。

在父 pom.xml 的 build/plugins 部分中包含 JaCoCo 插件:

<!-- JaCoCo Plugin for code coverage -->
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <version>0.8.7</version> <!-- Adjust version as needed -->
                <executions>
                    <execution>
                        <id>prepare-agent</id>
                        <goals>
                            <goal>prepare-agent</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>report</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>report</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>


目标在阶段prepare-agent中执行initialize,report目标在prepare-package阶段中执行。此配置可确保 JaCoCo 设置为在构建期间收集代码覆盖率数据。

在每个模块中配置 JaCoCo
在每个模块的 pom.xml 中,在 build/plugins 部分包含以下内容:

<!-- JaCoCo Plugin for code coverage -->
<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.7</version>
<executions>
        <execution>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
        </execution>
        <execution>
            <id>report</id>
            <phase>prepare-package</phase>
            <goals>
                <goal>report</goal>
            </goals>
        </execution>
    </executions>
</plugin>

这样可确保为每个子模块配置 JaCoCo,并分别生成覆盖率报告。

运行测试并生成报告
执行以下 Maven 命令来构建并生成代码覆盖率报告:
mvn clean install

此命令将编译代码、运行测试并生成 JaCoCo 报告。

运行上面的 Maven 命令后,您可以通过导航到web/target/site/jacoco/index.html和core/target/site/jacoco/index.html文件来查看代码覆盖率报告。

分析 JaCoCo 报告
JaCoCo 生成的报告包括线路覆盖范围、分支覆盖范围等。让我们看一下一些基本指标:

  • 线路覆盖率:表示测试覆盖的线路的百分比。
  • 分支覆盖率:衡量决策点的覆盖率,例如 if 语句和 switch 语句。
  • 指令覆盖率:表示测试覆盖的 Java 字节码指令的百分比。

结论
总之,将 JaCoCo 集成到 Maven 多模块项目中提供了一个强大的解决方案,可确保整个代码库的代码覆盖率。Maven 多模块项目的模块化结构与 JaCoCo 的详细报告相结合,使开发人员能够识别和解决应用程序中缺乏适当测试覆盖范围的区域。通过遵循本文中概述的步骤并探索各种 JaCoCo 配置,我们可以提高 Java 项目的质量和可靠性。


源码: Jacoco Coverage for Maven Multi-Module Project