1. 概述

本教程将展示当JaCoCo代码覆盖率低于特定阈值时,如何导致Maven构建失败。首先我们来看不带任何阈值的基础JaCoCo插件配置,然后添加一个新的执行目标来专门检查覆盖率。

我们将重点介绍这个新执行目标的关键元素。接着扩展一个简单的ProductService示例,观察添加BRANCH(分支)和INSTRUCTION(指令)覆盖规则后的效果。最后总结使用JaCoCo强制执行规则对质量控制的好处。

2. JaCoCo Maven插件

先以最简单形式使用JaCoCo插件。这意味着在执行mvn clean install时,插件会计算代码覆盖率并生成报告:

<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>${jacoco.version}</version>
    <configuration>
        <excludes>
            <exclude>com/baeldung/**/ExcludedPOJO.class</exclude>
            <exclude>com/baeldung/**/*DTO.*</exclude>
            <exclude>**/config/*</exclude>
        </excludes>
    </configuration>
    <executions>
        <execution>
            <id>jacoco-initialize</id>
            <goals>
                <goal>prepare-agent</goal>
            </goals>
        </execution>
        <execution>
            <id>jacoco-site</id>
            <phase>package</phase>
            <goals>
                <goal>report</goal>
            </goals>
        </execution>
    </executions>
</plugin>

3. 示例设置

接下来创建两个简单服务:CustomerServiceProductService。在ProductService中添加*getSalePrice()*方法,该方法包含两个分支(flag为true/false):

public double getSalePrice(double originalPrice, boolean flag) {
    double discount;
    if (flag) {
        discount = originalPrice - originalPrice * DISCOUNT;
    } else {
        discount = originalPrice;
    }
    return discount;
}

编写两个测试用例覆盖两种条件:

@Test
public void givenOriginalPrice_whenGetSalePriceWithFlagTrue_thenReturnsDiscountedPrice() {
    ProductService productService = new ProductService();
    double salePrice = productService.getSalePrice(100, true);
    assertEquals(salePrice, 75);
}

@Test
public void givenOriginalPrice_whenGetSalePriceWithFlagFalse_thenReturnsDiscountedPrice() {
    ProductService productService = new ProductService();
    double salePrice = productService.getSalePrice(100, false);
    assertEquals(salePrice, 100);
}

类似地,CustomerService包含简单方法*getCustomerName()*:

public String getCustomerName() {
    return "some name";
}

对应的单元测试:

@Test
public void givenCustomer_whenGetCustomer_thenReturnNewCustomer() {
    CustomerService customerService = new CustomerService();
    assertNotNull(customerService.getCustomerName());
}

4. 使用JaCoCo设置测试覆盖率基线

基础类和测试就绪后,先执行mvn clean install观察JaCoCo报告结果,建立当前模块测试覆盖率的基线:

successful build with jacoco

模块构建成功。查看JaCoCo报告:

jacoco report

当前覆盖率为72%,Maven构建成功。

5. 向JaCoCo插件添加规则

**现在添加规则:要求指令覆盖率不低于70%,分支覆盖率不低于68%**。在插件中定义新执行目标:

<execution>
    <id>check-coverage</id>
    <phase>verify</phase>
    <goals>
        <goal>check</goal>
    </goals>
    <configuration>
        <rules>
            <rule>
                <element>BUNDLE</element>
                <limits>
                    <limit>
                        <counter>INSTRUCTION</counter>
                        <value>COVEREDRATIO</value>
                        <minimum>0.70</minimum>
                    </limit>
                    <limit>
                        <counter>BRANCH</counter>
                        <value>COVEREDRATIO</value>
                        <minimum>0.68</minimum>
                    </limit>
                </limits>
            </rule>
        </rules>
    </configuration>
</execution>

关键点说明:

5.1. JaCoCo中的**

**标签定义规则应用的覆盖粒度级别。可选值包括:

  • BUNDLE:整个代码库的聚合覆盖率(示例中使用)
  • CLASS:类级别
  • LINE:代码行级别
  • METHOD:方法级别
  • PACKAGE:包级别

BUNDLE级别提供项目整体覆盖率视图,适合设置最高级别的覆盖规则。

5.2. JaCoCo中的**

定义一个或多个限制条件,每个**包含:

  • **:计数器类型(如INSTRUCTION)
  • **:度量方式(如COVEREDRATIO)
  • **:最小阈值

常用度量方式

  • COVEREDRATIO:覆盖比例(示例使用)
  • COVEREDCOUNT:已覆盖数量
  • MISSEDCOUNT:未覆盖数量
  • TOTALCOUNT:总数量

5.3. JaCoCo中的**

计数器类型决定覆盖率的计算维度:

  • INSTRUCTION:指令覆盖率(字节码指令)
  • BRANCH:分支覆盖率(if/switch等决策点)
  • LINE:代码行覆盖率
  • COMPLEXITY:圈复杂度
  • METHOD:方法覆盖率
  • CLASS:类覆盖率

核心计数器对比: | 计数器 | 作用 | 重要性 | |--------|------|--------| | INSTRUCTION | 衡量代码执行程度 | ⭐⭐⭐⭐ | | BRANCH | 确保true/false分支都被测试 | ⭐⭐⭐⭐ | | LINE | 精确到代码行 | ⭐⭐⭐ |

6. 覆盖率下降导致构建失败

为触发构建失败,禁用以下测试:

@Test
@Disabled
public void givenOriginalPrice_whenGetSalePriceWithFlagFalse_thenReturnsDiscountedPrice() {//...}

再次执行mvn clean install

failed maven jacoco rules

构建失败,错误信息:

[ERROR] Failed to execute goal org.jacoco:jacoco-maven-plugin:0.8.6:check (check-coverage) on project testing-libraries-2: Coverage checks have not been met.

失败原因分析

  • 指令覆盖率降至68%(要求≥70%)
  • 分支覆盖率降至50%(要求≥68%)

要修复构建,需恢复测试使覆盖率达标。

7. 结论

本文展示了如何通过JaCoCo在代码覆盖率低于阈值时使Maven构建失败。关键点包括:

强制执行覆盖率规则

  • 确保代码库始终满足最低测试覆盖要求
  • 阻止低覆盖率代码进入后续开发阶段

多维度覆盖控制

  • 指令覆盖率反映代码执行程度
  • 分支覆盖率确保决策点测试完整

质量保障机制

  • 识别测试不足区域
  • 降低低质量代码发布风险

⚠️ 实施建议

  • 从适中阈值开始(如70%)
  • 逐步提高要求
  • 结合代码审查使用

源码可在GitHub获取


原始标题:Fail Maven Build if JUnit Coverage Falls Below Certain Threshold