AI测试 AI 赋能测试:一次简单的 jacoco 二开记录

伊森 · 2025年08月21日 · 最后由 Lynn 回复于 2025年08月25日 · 2288 次阅读

本次二次开发需求:
已知项目 m 内,存在方法 mA,mB,mC 他们在 exec1 内都存在;修改代码后,删除了 mA,mC 内新增一个同级方法 mD,通过 jacoco 扫描后生成 exec2;代码对比时,由于对比结果会以 exec2 为主,忽视 mB 的代码统计。
本次修改结果需要取 exec1 和 exec2 的并集,取值结果需要 mB 的代码覆盖率也正常在 report 内展示

一、环境配置
检查下本地的 jdk 和 maven 版本,需要 JDK11+MAVEN3.6.3

二、代码拉取
jacoco 开源项目:xxx
测试用临时项目:xxx
三、项目结构
[图片]
根据代码覆盖率统计 这篇文章,我们找到本次需要修改方法是 cli 内的 report 方法。 根据这条信息,我们找到源代码:
针对这段代码,我发现他的主要判断都通过 CoverageBuilder 实现报告的对比,因此我针对 CoverageBuilder 这个方法进行了重构,引申出 UnionCoverageBuilder.java

通过 java 的三大特性之一,继承,我们新产生了一个子类,同时支持父类功能。
四、代码生成
4.1 使用 AI 写代码
AI 在本次源码修改中的作用,就是在我们理清我们需要执行的需求后,通过精准表达和二次沟通,实现他为我们写代码。我这里用的 gpt5 版本直接生产代码脚本,而我们所需要做的只有两点:代码逻辑的理解和调试;当这两者都做完并通过后,这块小需求也就可以直接上线了。
AI 在写代码时,针对单个类的代码块编写能力是强于人的;而针对多个类之间的调用是弱项;针对开源代码的修改,我们可以直接告诉它代码源头在哪,然后根据实际情况描述我们的逻辑最终生成代码

4.2 生成新包
为了长期区分我们二开后的 jacoco 包与常见开源包做区分,我添加了 01lb 包标识
通过执行 mvn clean install -DskipTests 后,我们生成了可以使用的 jar 包,jar 包默认在 target 文件夹下
将这个 jar 包上传到 maven,即可正常引用;
五、新功能测试
5.1 下载测试项目
git clone xxxx

5.2 获取 master 的第一次的报告
git checkout master
mvn clean test -DargLine="-javaagent:/Users/apple/Documents/tools/jacoco/jacocoagent.jar=destfile=jacocoA.exec"
cp ./jacocoA.exec /Users/apple/Documents/workspaces/comparefile/jacocoA.exec

5.3 获取修改后的代码报告
git checkout temp-branch
mvn clean test -DargLine="-javaagent:/Users/apple/Documents/tools/jacoco/jacocoagent.jar=destfile=jacocoB.exec"
cp ./jacocoB.exec /Users/apple/Documents/workspaces/comparefile/jacocoB.exec

5.4 生成代码对比报告
java -jar /Users/apple/Documents/tools/jacoco/org.jacoco.cli-0.8.7-01lb-nodeps.jar report \
../comparefile/jacocoD.exec ../comparefile/jacocoC.exec \
--classfiles target/classes \
--sourcefiles src/main/java \
--xml ../comparefile/union-report.xml \
--html ../comparefile/union-report

生成的测试报告在 union-report 下,点开 html 即可查看

六、总结
希望通过这篇文章,能学到
1、开源的代码如何运行
2、AI 赋能代码开发的简单应用

最佳回复

“并集” 报告可能存在的缺陷

  1. 报告与源代码版本不匹配,导致数据失真
    这是最核心的问题。代码覆盖率报告的每一行都必须能对应到一个具体的源代码文件。在作者的案例中,最终的 HTML 报告是基于 temp-branch (修改后) 的源代码生成的,但覆盖率数据却包含了来自 master (修改前) 分支的执行信息。

    • 场景一:覆盖了已删除的代码:方法 mAmCmaster 分支被执行并记录了覆盖率,但在 temp-branch 中它们已经被删除。最终报告在展示 temp-branch 的代码时,就无法显示 mAmC 的覆盖情况,这份来自旧版本的数据就成了 “幽灵数据”,只会影响总体的覆盖率百分比,但开发者找不到对应的代码。
    • 场景二:行号错乱:一个在两个版本中都存在的文件,如果在 temp-branch 中间被插入了几行代码,那么 master 分支记录的覆盖率行号就可能与 temp-branch 的实际行号对不上,导致报告将覆盖标记显示在错误的代码行上。
  2. 产生误导性的、虚高的覆盖率指标
    覆盖率的核心目标是评估当前版本代码的测试完善程度。将旧版本的测试结果合并进来,会人为地 “膨胀” 覆盖率数字。

    • 例如,一个模块在旧版本中有 100% 的覆盖率。在新版本中,开发者对其进行了重构,但忘了更新测试,导致实际覆盖率下降到 50%。如果使用 “并集” 报告,旧的 100% 数据可能会掩盖新版本测试不足的事实,让团队产生 “这个模块测试很充分” 的错觉,从而忽略了潜在的质量风险。
  3. 违背了代码覆盖率的 “快照” 哲学
    一次覆盖率报告应该是某个时间点上 “代码状态” 与 “测试状态” 的一个精确快照。它回答的问题是:“对于这份代码,我们的这套测试跑了多少?”
    而 “并集” 报告混合了两个不同时间点的快照,回答的是一个模糊的问题:“对于现在的代码,我们曾经跑过的、来自不同版本的测试加起来能覆盖多少?” 这个指标的指导意义就变得不那么明确了。

为什么 JaCoCo 原来不这么做?(其设计考量)

  1. 保持确定性和单一事实来源
    JaCoCo 的设计遵循业界标准:一份报告严格对应一个代码版本和一次(或多次)针对该版本的测试执行。这样可以确保报告的结果是确定性的、可追溯的。开发者看到报告中的任何数据,都能准确地在对应的 Git Commit 中找到源代码和测试用例。

  2. 聚焦于 “增量覆盖率” 和变更影响
    在现代 CI/CD 流程中,团队更关心的是“本次变更所引入的代码是否得到了充分测试”,即 “增量覆盖率”。JaCoCo 的标准用法能够很好地支持这一点。开发者在一个 Pull Request 中修改了代码,CI 会针对这个分支跑一次测试并产生报告,团队可以清晰地看到新代码的覆盖情况。如果合并历史数据,就无法有效评估当前变更的质量。

  3. 避免工具的复杂性
    如果官方支持这种 “并集” 功能,就需要处理大量复杂的边界情况,例如如何对齐不同版本的行号、如何处理被重命名的类和方法、如何向用户清晰地展示数据来源等。这会让工具本身变得非常复杂,且容易产生让用户困惑的结果。保持简单、专注于核心场景是优秀开源工具的普遍设计原则。

共收到 1 条回复 时间 点赞

“并集” 报告可能存在的缺陷

  1. 报告与源代码版本不匹配,导致数据失真
    这是最核心的问题。代码覆盖率报告的每一行都必须能对应到一个具体的源代码文件。在作者的案例中,最终的 HTML 报告是基于 temp-branch (修改后) 的源代码生成的,但覆盖率数据却包含了来自 master (修改前) 分支的执行信息。

    • 场景一:覆盖了已删除的代码:方法 mAmCmaster 分支被执行并记录了覆盖率,但在 temp-branch 中它们已经被删除。最终报告在展示 temp-branch 的代码时,就无法显示 mAmC 的覆盖情况,这份来自旧版本的数据就成了 “幽灵数据”,只会影响总体的覆盖率百分比,但开发者找不到对应的代码。
    • 场景二:行号错乱:一个在两个版本中都存在的文件,如果在 temp-branch 中间被插入了几行代码,那么 master 分支记录的覆盖率行号就可能与 temp-branch 的实际行号对不上,导致报告将覆盖标记显示在错误的代码行上。
  2. 产生误导性的、虚高的覆盖率指标
    覆盖率的核心目标是评估当前版本代码的测试完善程度。将旧版本的测试结果合并进来,会人为地 “膨胀” 覆盖率数字。

    • 例如,一个模块在旧版本中有 100% 的覆盖率。在新版本中,开发者对其进行了重构,但忘了更新测试,导致实际覆盖率下降到 50%。如果使用 “并集” 报告,旧的 100% 数据可能会掩盖新版本测试不足的事实,让团队产生 “这个模块测试很充分” 的错觉,从而忽略了潜在的质量风险。
  3. 违背了代码覆盖率的 “快照” 哲学
    一次覆盖率报告应该是某个时间点上 “代码状态” 与 “测试状态” 的一个精确快照。它回答的问题是:“对于这份代码,我们的这套测试跑了多少?”
    而 “并集” 报告混合了两个不同时间点的快照,回答的是一个模糊的问题:“对于现在的代码,我们曾经跑过的、来自不同版本的测试加起来能覆盖多少?” 这个指标的指导意义就变得不那么明确了。

为什么 JaCoCo 原来不这么做?(其设计考量)

  1. 保持确定性和单一事实来源
    JaCoCo 的设计遵循业界标准:一份报告严格对应一个代码版本和一次(或多次)针对该版本的测试执行。这样可以确保报告的结果是确定性的、可追溯的。开发者看到报告中的任何数据,都能准确地在对应的 Git Commit 中找到源代码和测试用例。

  2. 聚焦于 “增量覆盖率” 和变更影响
    在现代 CI/CD 流程中,团队更关心的是“本次变更所引入的代码是否得到了充分测试”,即 “增量覆盖率”。JaCoCo 的标准用法能够很好地支持这一点。开发者在一个 Pull Request 中修改了代码,CI 会针对这个分支跑一次测试并产生报告,团队可以清晰地看到新代码的覆盖情况。如果合并历史数据,就无法有效评估当前变更的质量。

  3. 避免工具的复杂性
    如果官方支持这种 “并集” 功能,就需要处理大量复杂的边界情况,例如如何对齐不同版本的行号、如何处理被重命名的类和方法、如何向用户清晰地展示数据来源等。这会让工具本身变得非常复杂,且容易产生让用户困惑的结果。保持简单、专注于核心场景是优秀开源工具的普遍设计原则。

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册