通用技术 jacoco 代码覆盖率使用中遇到的一些坑

Ramsey · 2018年11月19日 · 最后由 jacky 回复于 2021年02月22日 · 9328 次阅读

今年 5 月离开某易来到新公司之后,一直希望把某易的代码覆盖率带过来,一方面可能之前在某易测试的时候习惯了提测后再检查代码覆盖率来进行补充测试的模式,另一方面新公司是家算不大的中小公司,在测试方面还是一直走比较传统的手工测试的路线,也希望可以改进一下,向精准化测试推进。
代码覆盖率(jacoco)的原理和具体搭建流程网上资料很多,我这里就不详细在陈述了,我这边搭建也是主要按照网上的流程通过 jenkins+jacoco 配置。起初在看到覆盖率数据正常获取,并验证了初步覆盖率结果的准确性后,便把它投入到项目中进行使用。但是在运行一段时间后,组织开发与测试同学一起进行覆盖率分析时还是发现实际数据与真实结果存在一定差异的问题。

首先基于 jacoco 工具本身应该不会存在覆盖率丢失和遗漏的情况,那么为了找到差异的原因,我这边对整个覆盖率统计的过程进行了复盘。
我们是通过 jenkins+jacoco 配置代码覆盖率,设置每天 15 点定时,通过 jenkins 任务去 git 上拉取服务代码,并对代码进行编译生成 class 文件。由于覆盖率统计和项目发布任务都在同一台 jenkins 机器上,所以编译的 jdk 是一致的,确保 class 文件不会有差异。然后通过 ant build.xml 文件去 dump 最新的 exec 文件,并通过 merge 保证覆盖率数据一直增量记录。最后通过 jacoco 插件实现覆盖率数据的正常展示。
表面上我当初搭建的时候觉得这样的安排很合理,但是当细细分析时,还是存在很多遗漏点:
1.覆盖率数据出现丢失:
我们 jenkins 覆盖率任务是每天 17 点定时去 dump 获取 exec 文件,但是我们知道 jacocoagent 由于注入在服务中,随服务的关闭而关闭,所以当服务发布重启时覆盖率数据会出现丢失,比如:服务 A 每天分别在 9 点和 19 点发布 2 次,那我们定时去 dump exec 文件是,明显会丢失从 19 点到 9 点那段时间的覆盖率,而且实际测试过程中由于项目偏敏捷模式,每天的发布频率并无法控制。而且实际除了发布重启之外也可能存在服务器挂掉的情况。
所以为了尽可能的全面的获取到覆盖率数据,我们需要提高 dump exec 的频率。在 jenkins 定时任务不变的情况下,通过编写 python 脚本来实现每 15 分钟去执行 ant build.xml 命令去 dump 最新的覆盖率数据。实际当然也可以把频率提升的更高。这样的方式,可以尽可能规避掉覆盖率数据丢失的情况。
2.覆盖率数据不准确:
大家可能会觉得奇怪怎么区分出覆盖率不准确和丢失的情况,其实是这样的,覆盖率丢失是之前操作了但是没有记录为已覆盖,但是再次操作会记录为已覆盖。覆盖率不准确则是不管怎么操作对应代码都不会记录为已覆盖。基于始终相信 jacoco 工具是不会有错的原则,所以我们可以把问题的焦点放在最容易出现不准的本地与服务端的 class 文件不一致的情况。
jenkins 上的 class 文件是通过 git 上拉取的源码编译而成,由于前期规避了 jdk 版本不一致的问题,所以觉得本地的 class 文件肯定与服务端的保持一致。但是当切入到实际使用时还是存在遗漏。我们每次运行覆盖率任务时从 git 上拉取的都是最新的代码,所以编译的 class 文件也是基于最新的代码。但是实际 dump 过来的 exec 基于的未必是最新的代码,还是因为项目偏敏捷的模式,每天 commit 的代码量会比较大,为了保证项目正常测试不会频繁被打断,所以并不会实时 commit 新代码后立即去发布,这样就导致了你 dump 过来的 exec 是基于之前的版本的代码,从而本地和服务端的 class 是不一致的情况。
当我们认识到这个问题,其实解决也不难,从 git 上拉取代码,改为从 jenkins 上对应服务的发布任务中去拷贝源码和 class 文件,这样可以保证本地和服务端的始终都是一致的。

这些问题并不是很复杂,但是如果你只是按照教程去配置代码覆盖率还是会遇到的一些因为实际使用场景而无法预期的坑。所以记住配置说明文档也不是万能的。

共收到 21 条回复 时间 点赞

这两个点确实是实践中需要特别留意的。不准确的覆盖率很容易在前期失去用户的信任。

全量代码覆盖率? 针对于第一个问题 15 分钟 job 拉取还是会有覆盖率数据丢失的问题,建议在部署前主动拉取覆盖率数据,Jenkins + jacoco 太不灵活了,建议把拉取覆盖率、生成报告全部封装成 service 服务对外提供接口,比如拉取覆盖率封装成 http 接口(做成异步的),这样就可以很好的嵌入 CI 流程里

可否把 dump 的任务放在服务部署的 job 步骤里面,build.xml 里面设置为 append 追加模式
确保每次服务停止或重启时都会 dump 数据。

莱亚 回复

首先要你在重启或者服务停止前去 dump 数据,当然你的方案也是可以的。
但是有个风险就是,如果服务自己重启。如果你的服务比较稳定,我觉得也是可行的。
当然这个方案也是基于你每天有个定时任务去执行 dump 的基础上,毕竟你不可能指望要等到发布服务的时候才去 dump 覆盖率数据

从 git 上拉取代码,改为从 jenkins 上对应服务的发布任务中去拷贝源码和 class 文件,这样可以保证本地和服务端的始终都是一致的。----------------这句话没有理解,jenkins 不是也是从 git 上去拉的代码么?

是的,但是比如你现在发布的 1.1 版本(因为不是每次开发代码提交,都会立即进行代码发布),你 git 上提交的已经是 1.3 版本,这时候你直接去 git 上拉,实际你会发现你拉取的代码和实际运行的代码是不一致的

关于第二点,我理解的是同样的代码分支,运行着的可能代码是老版本的,而 git 拉取的是新版本的,导致代码不一致;但是我有个疑问,就算当时的代码一致了,但是后续这个分支又有代码提交,导致之前 dump 的数据还是和最新的代码不一致,还是会导致覆盖率不准确吧?

对于第二个问题,我尝试了一下,发现,每次新提交代码之后,就算保持 class 文件一致,再去生成报告发现之前已经覆盖的语句还是变为没覆盖。。不知楼主有没有遇到类似的情况呢〜〜

Ramsey #10 · 2020年02月24日 Author
cc 回复

你是每次拉覆盖率 exec 文件的时候,把之前的覆盖率文件删掉了吧。因为 jacoco-agent 是注入在服务中的,当你服务提交新代码发布重启后,注入的 jacoco-agent 也会重启,之前统计的覆盖率也会被清掉。所以你要每次拉取新的覆盖率后和之前覆盖率结果进行 merge,做增量记录

Ramsey 回复

楼主你好,:
我操作步聚如下
1、第一次源码文件有一个方法,测试使其已覆盖,并拉取 exec 数据,生成的报告可以看到相关的代码已被覆盖。
2、编辑源码文件,新增一个方法,重新编译打包并启动这个项目,测试使新增代码覆盖,并拉取 exec 数据,但是这次生成的报告,只看到了新代码被覆盖,以前覆盖的代码(1 中代码)没有被覆盖。
而且我 reset 是 false,append 是 true,也就是 exec 文件是有以前的覆盖率数据的。后面我试了一下:
1、不改代码去重新启动项目,再生成报告,原覆盖率还在
2、不改代码去重新编译打包,生成报告,原覆盖率还在
但是一旦原代码有新增代码,并没有改变原来的代码,原覆盖率也会不存在了。
期待楼主的回复〜

12楼 已删除
Ramsey 回复

因为现在一个版本会有多次代码更新和修改,这样没办法保存所有的覆盖率数据,除非每次发版后都把用例全跑一次。。。
期待你的回复〜

Ramsey 回复

我应该找到答案了,新增代码之后重新编译的字节码文件跟之前不一样了,所以想要获取之前执行过的覆盖率数据,得把所有用例都执行一次。目前只能是这样了。
附上答案链接:https://groups.google.com/forum/?fromgroups=#! topic/jacoco/NneHas2oAdE
不知道你们在实际使用过程当中怎么规避这种情况发生,因为每次开发修改 bug 重新布署之后,之前的覆盖率就会丢失,就算那部份代码并没有被修改。

Ramsey #15 · 2020年02月25日 Author
cc 回复

👍

Ramsey 回复

所以,这样来看,在敏捷开发中进行手工测试,是没办法比较好的收集覆盖率数据咯,更适用于自动化测试。对于手工测试时的覆盖率分析,楼主以前公司是怎么做到的呀,求指教〜

Ramsey #17 · 2020年02月26日 Author
cc 回复

敏捷测试中,我们更多的是关注每个迭代的变更代码的覆盖率,也就是每次代码提交后,对这块改动代码是否覆盖,至于没有改动的代码,我们可以通过每次自动化去回归保证覆盖率。关于变更代码覆盖率你可以看下我的这篇文章:https://testerhome.com/topics/19077

Ramsey 回复

其实已经拜读过了,不知楼主是否方便加个 qq 或微信,我还有一点点疑问😁 ,打扰了😹

Ramsey #19 · 2020年02月26日 Author
cc 回复

可以

僅樓主可見

针对微服务多个副本的情况下,该如何处理; 可能请求分发到不同副本上去, 但是你拉去 exce 时也是在不同 pod 上,会不会由于负载均衡机制导致丢失数据文件, exec 文件做合并吗?

22楼 已删除
23楼 已删除
Ramsey 回复

变更迭代的覆盖率 也会出现上面的问题:修改代码后覆盖率从 0 开始。
开发提交代码,测试只验证了 2 个功能;开发再提交代码,覆盖率就变成 0 了;这种怎么处理?

cc 回复

请问问题解决了吗?即使是差异覆盖率,修改代码后覆盖率也都清零了把,怎么处理的?谢谢

需要 登录 後方可回應,如果你還沒有帳號按這裡 注册