1. 概述

代码覆盖率 是一种软件度量指标,用于衡量在自动化测试期间执行了多少行代码。

本教程将带你实战了解 JaCoCo,一个专为 Java 项目设计的代码覆盖率报告生成工具。

2. Maven 配置

要快速上手 JaCoCo,需要在 pom.xml 文件中声明以下 Maven 插件:

<plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.7.7.201606060606</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>

链接指向 Maven 中央仓库的最新插件版本(实际使用时建议检查最新版本)

3. 代码覆盖率报告

在探索 JaCoCo 的覆盖率功能前,先准备一个代码示例。这是一个简单的 Java 函数,用于判断字符串是否为回文:

public boolean isPalindrome(String inputString) {
    if (inputString.length() == 0) {
        return true;
    } else {
        char firstChar = inputString.charAt(0);
        char lastChar = inputString.charAt(inputString.length() - 1);
        String mid = inputString.substring(1, inputString.length() - 1);
        return (firstChar == lastChar) && isPalindrome(mid);
    }
}

配套一个简单的 JUnit 测试:

@Test
public void whenEmptyString_thenAccept() {
    Palindrome palindromeTester = new Palindrome();
    assertTrue(palindromeTester.isPalindrome(""));
}

运行 JUnit 测试时会自动触发 JaCoCo 代理,在 target/jacoco.exec 生成二进制格式的覆盖率数据。

⚠️ 直接阅读二进制文件不现实,但工具如 SonarQube 可以解析这些数据。

好消息是,通过 jacoco:report 目标可以生成可读性强的报告(支持 HTML/CSV/XML 格式)。查看 target/site/jacoco/index.html 会看到类似这样的报告:

覆盖率总览图

图:项目级覆盖率报告,展示整体指标

点击报告中的 Palindrome.java 链接,可以深入查看每个类的详细覆盖情况:

类级覆盖率详情图

图:类级覆盖率详情,用颜色标识未覆盖/部分覆盖/完全覆盖的代码

💡 在 Eclipse 中可通过 EclEmma 插件 零配置实现相同效果

4. 报告分析

当前报告显示:

  • 指令覆盖率 21%
  • 分支覆盖率 17%
  • 圈复杂度 3/5

JaCoCo 报告中的 38 条指令指的是字节码指令,而非 Java 源码行数。

JaCoCo 通过颜色编码直观展示覆盖率:

  • 红色菱形:分支完全未覆盖
  • 黄色菱形:分支部分覆盖
  • 绿色菱形:分支完全覆盖

行覆盖率使用相同的背景色方案。核心指标包括:

指标 说明
行覆盖率 基于测试调用的字节码指令数量计算
分支覆盖率 重点关注 if/elseswitch 语句的分支覆盖情况
圈复杂度 表示代码路径的线性组合数量,反映需要多少测试用例才能覆盖全路径

举个简单例子:没有条件语句的代码圈复杂度为 1(只需一条执行路径)

5. 核心原理

JaCoCo 以 Java 代理 模式运行,核心职责是在测试执行时动态修改字节码。它能深入每条指令,追踪测试期间哪些代码被执行过。

技术实现上:

  1. 使用 ASM 框架进行实时代码插桩
  2. 通过 JVM Tool Interface 接收事件通知

架构原理图

图:JaCoCo 代理与 JVM 的交互架构

高级用法:支持以服务器模式运行代理,通过 jacoco:dump 目标触发数据导出

更深层设计细节可参考 官方文档

6. 覆盖率提升实战

现在我们着手提升覆盖率分数。根据初始报告的缺失覆盖点,补充以下测试:

@Test
public void whenPalindrom_thenAccept() {
    Palindrome palindromeTester = new Palindrome();
    assertTrue(palindromeTester.isPalindrome("noon"));
}
    
@Test
public void whenNearPalindrom_thenReject(){
    Palindrome palindromeTester = new Palindrome();
    assertFalse(palindromeTester.isPalindrome("neon"));
}

执行 mvn jacoco:report 生成新报告:

提升后总览图

图:补充测试后覆盖率显著提升

类级详情显示所有代码已完全覆盖:

完全覆盖详情图

图:绿色标识所有分支/路径已被测试覆盖

✅ 覆盖率门禁检查

实际项目中需要持续监控覆盖率。JaCoCo 支持设置最低覆盖率要求,未达标时构建失败:

<execution>
    <id>jacoco-check</id>
    <goals>
        <goal>check</goal>
    </goals>
    <configuration>
        <rules>
            <rule>
                <element>PACKAGE</element>
                <limits>
                    <limit>
                        <counter>LINE</counter>
                        <value>COVEREDRATIO</value>
                        <minimum>0.50</minimum>
                    </limit>
                </limits>
            </rule>
        </rules>
    </configuration>
</execution>

示例要求行覆盖率不低于 50%

执行 mvn clean verify 触发检查(jacoco:check 默认绑定到 verify 阶段)。不达标时会报错:

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

7. 总结

本文介绍了如何使用 JaCoCo Maven 插件生成 Java 项目的代码覆盖率报告。但需要谨记:

⚠️ 100% 覆盖率 ≠ 有效测试,它仅反映代码的执行程度。更科学的测试有效性评估可参考 变异测试(如 PITest)。

完整示例代码见 GitHub 项目


原始标题:Intro to JaCoCo

« 上一篇: JSONForms简介
» 下一篇: Orika 映射框架详解