马蜂窝技术原创内容,更多干货请关注公众号:mfwtech
测试覆盖率常被用来衡量测试的充分性和完整性,也是测试有效性的一个度量。「敏捷开发」的大潮之下,如何在快速迭代的同时保证对被测代码的覆盖度和产品质量,是一个非常有挑战性的话题。
在马蜂窝大交通、酒店等交易相关业务中,项目的开发和测试实践同样遵循敏捷的原则,迭代周期短、速度快。因此,如何依据测试覆盖率数据帮助我们有效判断项目质量、了解测试状态、提升迭代效率,是我们一直很重视的工作。
对于功能测试而言,通常可以通过充分了解需求、完善的测试用例、接口测试、Review 技术方案等来保证测试充分性。但随着业务规模快速发展,业务逻辑越来越复杂,系统级别交互越来越多,这些方法都不能保证所有的代码一定被全部测试过,也给测试人员带来极大挑战。
说到这儿和大家分享一个因为测试覆盖不充分,影响到线上业务的真实案例。事件起因是项目提测阶段一个微服务 Sonar 扫描没通过,开发同学为了修复 Sonar 发现的问题而重构了一部分历史代码,却导致一个原有发券需求的错误。当天下午运营触发发券后 Bug 出现直接导致生单不可用,并且持续了将近一个小时。这里的问题就是发券功能与此次需求无关,开发人员修改代码后测试人员不知道,也没有经过测试,但跟随本次需求一起上线了,测试用例无法覆盖到。
通过这个例子可以明显地看到正确统计被测代码覆盖率的重要性。当前市面上统计覆盖率的工具很多,但应用到我们的实际场景中通常存在以下问题:
因此,大交通测试组决定自研工具进行被测代码的覆盖率统计,用代码覆盖结果反向地检查测试人员测试用例的覆盖度和开发人员代码的冗余度,更精准地定位和分析问题,保证产品质量。
结合我们的实际场景,覆盖率统计工具的实现目标如下:
我们将覆盖率统计工具命名为 jCover,并将其引入 CI/CD 体系,与内部 Java 平台项目管理及持续集成系统 MONE 打通。工具设计如下图所示:
图 1:覆盖率工具整体架构图
覆盖率统计的大致流程为首先通过获取多环境服务的配置信息,生成指定环境服务的测试覆盖率文件,然后根据需要生成全量覆盖率报告和增量覆盖率报告,最后,存储测试覆盖率报告到指定环境目录下。
jCover 的主要功能包括:
下面围绕以上功能点,为大家介绍 jCover 的作用、特点和具体实现方式。
为了简化操作步骤降低学习成本、报告简单易懂,jCover 支持界面化操作,可以手动生成测试覆盖率报告和查询测试覆盖率报告;查询测试覆盖率报告入口统一,代码测试覆盖率数据清晰,方便数据统计和量化。
图 2:覆盖率报告查询页面
由于使用敏捷迭代,项目多、并行率高,大交通所有微服务均引入测试环境隔离插件,同时支持多项目并行测试。因此,jCover 需要支持多环境并行的测试覆盖率统计。
具体来说,每个测试项目都会有一个单独的环境标识,以隔离环境标识+环境类型+服务名称唯一确定一个覆盖率统计单元;每一个覆盖率统计单元都会生成对应的测试覆盖率统计报告,测试覆盖率统计报告被存储到 jCover 部署的服务器中,存储位置是环境标识对应的服务目录下;同一个服务的不同提测分支同时在多项目中测试时相互间的覆盖率报告不会被覆盖。
多环境并行流程图见下图(目前只支持 QA 环境覆盖率统计):
图 3:多环境并行流程图
例如:隔离环境 gjssqz 和隔离环境 supplyrefund 可以同时进行测试覆盖率统计,隔离环境标识+环境类型+服务名称指定了每个环境下的单个服务测试覆盖率统计结果。
覆盖率工具 UI 展示如下图:
图 4:用「隔离环境标识+环境类型+服务名称」来标识和统计覆盖率
在测试时有时候需要关注全量代码的测试覆盖率,代码全量覆盖率统计过程如下:
(1)查询服务信息
jCover 通过内部 Java 平台项目管理及持续集成系统 MONE 来查询被测微服务的信息:
(2)收集覆盖率数据
为了做覆盖率统计,需要从微服务部署的容器中下载测试覆盖率 Dump 文件。我们采用的解决方案是使用 JavaAgent 代理,首先对微服务部署的容器进行了改造,使得容器支持 JavaAgent 功能;其次为了动态插桩记录被测代码的运行结果,在被测微服务的 JVM 启动脚本中增加 JavaAgent 参数。
覆盖率工具根据 IP 和监听端口通过 JavaAgent 代理从容器中下载 Dump 文件,以环境和服务标识唯一 Dump 文件,并存储到 jCover 所在的服务器。在下载覆盖率数据时采用追加方式,如果该 Dump 覆盖率文件已存在,那么该轮的覆盖率数据会直接在文本末尾进行追加,便于统计整个测试过程的代码覆盖结果。
(3)根据全量覆盖率文件生成全量覆盖率报告
用户打开测试覆盖率工具前端界面就可以在浏览器中查看步骤 D 生成的覆盖率报告了,绿色表示被覆盖,红色表示未被覆盖,黄色表示部分覆盖:
图 5:全量测试覆盖率报告展示
业务发展到一定阶段后,代码量级很大。主干代码我们默认是正确的代码。当一个新需求提测后,测试人员更关注的其实是针对本次需求更改的代码的覆盖率而不是全部代码的覆盖率,想从全量覆盖率报告中找出本次更改的增量代码的覆盖率是非常困难的。基于此问题我们开发了增量覆盖率统计功能。
增量覆盖率统计和全量覆盖率统计有三个相同的步骤:查询服务信息、收集覆盖率数据、生成全量覆盖率文件。生成全量覆盖率文件后,增量覆盖率统计的不同点是增加了以下处理:增量代码获取、生成增量覆盖率报告,具体实现如下:
(1)Diff 增量代码
(2)生成增量覆盖率报告
图 6:增量覆盖率流程
下图是交易服务增量覆盖率报告的截图示例,从图中清晰看出增量行和更新行的测试覆盖率情况。
图 7:增量覆盖率报告展示
当测试人员需要查看最新的测试覆盖率时,可以通过「jCover - 生成测试覆盖率报告」的入口手动触发生成测试覆盖率报告。
图 8:手动统计覆盖率图形化界面
在一个需求的实际测试中被测的微服务通常有多个。因为修改 Bug 等原因,被测微服务经常需要重启,重启后容器销毁,JavaAgent 插桩后的文件也会被删除导致重启前覆盖的代码无法被统计。
如果每次重启每个被测微服务前都需要测试人员手动生成覆盖率报告的话,是个很大的工作量也会经常被遗忘。因此 jCover 通过和发布系统结合实现了覆盖率自动统计功能。具体实现方法为 jCover 提供了自动生成覆盖率文件接口,在服务重启部署时后端发布系统调用此接口,实现自动统计覆盖率,不管服务重启多少次都可以把每次测试时覆盖的代码全部统计到。
之前大交通的后端服务发布系统为 Jenkins,后续升级为了 MONE,两种发布系统下均支持服务部署时自动统计覆盖率。
经过一段时间推广使用和优化,现在大交通所有的需求提测后均使用 jCover,主要体现出以下几点优势:
目前 jCover 只支持查看每一个类覆盖行和未覆盖行是什么,但是对于一个微服务整体行覆盖率是多少没有统计,我们正在开发增量覆盖率百分比,帮助开发和测试做出准确的判断。目前也在调研前端的覆盖率统计,下一步将实现前端的覆盖率统计工具 jCover。
在日常需求测试中可以采用测试(手工测试、接口测试、回归测试等)+jCover 覆盖分析的测试方法来完善测试场景,减少测试遗漏,确保测试的充分性,但需要注意的是 jCover 统计只能展示哪些代码被覆盖了,代码的正确性还是需要用例的执行结果正确来保障;还有时候开发会漏开发某部分需求,此时依靠 jCover 是无法发现这部分遗漏代码的,因此除了 jCover 之外可以通过参与技术评审、编写用例时参考产品功能矩阵图等多种手段发现问题,全方位保障被测代码的质量。
以上就是对马蜂窝大交通测试覆盖率统计工具 jCover 的分享。当然,统计代码覆盖率仅仅是一种手段,覆盖率高不一定代表质量好,但覆盖率不高的代码质量风险肯定很高。只有清楚在覆盖率统计数据背后反映出的问题,才能从根本上保证软件整体的质量。
本文作者:代春美,马蜂窝测试部-交易测试工程师;孙海燕,马蜂窝测试部-交易测试团队负责人。