内容字号:默认大号超大号

段落设置:段首缩进取消段首缩进

字体设置:切换到微软雅黑切换到宋体

唯品会iOS代码覆盖率的应用实践

2019-02-10 21:03 出处:清屏网 人气: 评论(0

 背    景 

唯品会特卖会App承载着前端选购、下单、支付等核心业务,业务逻辑异常复杂,以iOS端为例,代码行数已达50万。2018年初,我们启动了研发流程的优化,大版本迭代周期由3周缩减至2周,伴随而来的是测试独占时长的压缩。如何在更短的测试时间内保证App代码质量是对测试团队一次挑战。长期以来,黑盒测试依赖于测试人员的能力和业务熟悉度,测试质量的高低没有客观数据可衡量。推进精准测试是我们的必经之路,而代码覆盖率是必须要跨过的槛,有见及此,特卖会App团队着手研究iOS及Android代码覆盖率的应用落地。本文重点讲解iOS代码覆盖率的应用实践。

调研中发现,iOS代码覆盖率如要大规模应用到功能/回归测试,需解决如下三个痛点:

  • 苹果开发工具Xcode仅支持单元测试的代码覆盖率,而且只能在本地连线调试。而实际的测试设备多达20+,把设备连接电脑再收集覆盖率是不可行的,我们必须做到收集自动化,而且对用户无感知。

  • App的功能分支测试周期大约有4天,此期间可能存在多次的开发代码提交、缺陷修复,以哪一个版本作为覆盖率统计是值得思考的。我们尝试约定在某个稳定版本做全面的功能测试,但效果并不好,因为前期已测过的功能没必要重测一遍,时间也不允许。因此,我们需要支持行覆盖率的多版本合并。

  • 鉴于app的运行模式仅支持离线插桩,我们需要处理大量手机用户,不同时间节点的覆盖率数据。

技术选型 

首先,代码覆盖率要考虑插桩,插桩方式有3种:1)源码插桩;2)中间代码插桩;3)可执行文件的二进制插桩。iOS使用clang作为前端编译器,负责生成AST语法树,并将代码编译成LLVM bitcode;而LLVM作为后端编译器负责生成平台相关的机器语言。其编译插桩过程属于中间代码插桩,简化的过程可参考图1。LLVM脱胎于GCC,也可使用gcda/gcno记录代码覆盖率。其中:

  • gcno是notes file,插桩编译后生成;

  • gcda是data file,与gcno对应,由程序执行时记录;

LCOV作为GCC Code Coverage的开源前端工具,支持行/函数/分支覆盖的报告输出。因此,我们使用LCOV作为iOS代码覆盖率生成工具。

图1: 插桩编译原理

iOS覆盖率 工具整体框架如下:

图2: 系统架构

  • 前端UI 接入公司的覆盖率平台,支持覆盖率的收集、生成报告、邮件通知;

  • 调度层 负责分发消息至具体执行的机器 AnalyserAnalyserjenkins 封装 , 节点的机器必须为 Mac 系统,物理机或虚拟机均可;

  • 分析器 支持覆盖率文件的合并、差异以及报告的输出。另外分析器负责接收终端上报覆盖率数据;

  • 文件系统 管理覆盖率文件的中间数据以及版本构建信息

 实现原理 

我们设计出一整套解决方案,请看图3

图3:iOS代码覆盖率实现时序图

具体处理时序如下:

1) Xcode打包服务器在CI中自动插桩,并根据commit id,上传与之对应的标签文件gcno;

2) 用户通过OTA方式安装app,在测试过程中只需把app置于后台,覆盖率数据即可从缓存刷入硬盘,并通过http接口上传至文件服务器;

3) 覆盖率平台可通手工触发方式,收集指定分支、commit id的覆盖率文件info;

4) 待预处理完毕后,覆盖率平台可生成合并的或差异的覆盖率报告;

接下来我们从如下三方面剖析技术实现:

  • Xcode插桩

  • Pod上传服务器

  • 覆盖率文件的合并/差异处理

(一)    Xcode插桩

在BuildSettings分别设置Instrument Program Flow、Generate Legacy Test Coverage File为True,即可打开插桩。

图4: Xcode 插桩设置

(二)    Pod上传服务器

通过Pod的插件方式,我们使用C++混编,调用外部的__gcov_flush方法把覆盖率gcda从内存刷到硬盘。为了方便交叉编译,GCC设计了两个与生成路径相关的变量GCOV_PREFIX,GCOV_PREFIX_STRIP,如下图。

图5: Pod的插件实现

在生成GCDA后就要考虑上传到文件服务器了,我们采用AFNETWORKING库,把多个GCDA文件整合在一个接口上传,文件大小约7M Bytes

(三)    覆盖率文件预处理,支持差异/多版本合并

我们使用开源LCOV来处理文件,gcda+gcno+源码可生成中间文件info,info文件包含了最终呈现报告的所有信息,包括源码路径、函数名、函数执行次数、函数总数、函数在源文件的位置、行号、行执行次数、行总数。为支持行差异覆盖率,我们自定义了新字段CA/CF/CH,请见图6。

图6:  中间文件info数据结构

对于两个不同版本的合并,使用平移。原则是把旧版本代码里的未变更的行覆盖平移到新版本。请见图7。

图7: 新旧版本git diff对比

具体流程如下,旧代码的覆盖率与git diff 比较,先判断是否块内代码,如不是块内代码则平移行号至新版本;若是块内代码,则再判断是否为已删除的行号,若是,则跳过不做平移;若不是已删除的行,则平移行号,请见图8。或许读者有疑问,两个版本的上下文都变了,平移行覆盖率后的可信度高么?笔者想强调的是,覆盖率报告好比一份健康检查报告,只关心异常点,没有覆盖的行就是异常点。在使用平移算法后那些依然没有覆盖的行就是没经探索的荒地,将是重点关注的对象。

图8: 两个版本的行覆盖率合并流程

我们再扩展至n个版本的合并,可沿用以上的平移算法,只需把最后一个版本作为平移的基准版本即可,可参考下图。

图9:  多版本的行覆盖率合并流程

至此,iOS覆盖率整套实现方案描述完毕。

 实践效果 

iOS覆盖率工具已接入至覆盖率平台,可通过服务器管理指定待收集的系统平台,分支;平台支持多分支同时收集。功能测试分支的覆盖率收集时间可控制在20分钟内。覆盖率收集完成后,平台可设定行差异覆盖率的对比基线,可设定一个分支内合并的多个版本,效果请见图10、11、12。

图10:  移动测试列表服务器管理

图11:  生成覆盖率报告页面

图12:  iOS覆盖率报告的目录结构

最后,我们看看该工具的应用情况,特卖会App当前每两周发一个版本,在功能测试阶段,各条业务线(包括特卖/基础/用户/创新)已开始使用iOS覆盖率检测工具,从使用的经验看,功能测试阶段的代码行差异覆盖率达到90%,未覆盖的行主要包括三项,风险可控:

  • 异常捕捉

  • 非空指针的保护

  • 冗余预留代码

 精准测试方法总结 

结合iOS覆盖率工具,我们总结出目前适合特卖会app的精准测试策略,请见图13。在测试分析阶段,我们注重需求差异,技术差异的分析,结合用例地图得出测试策略,并指导我们进行冒烟/功能测试,每个测试阶段的Code Review着重点有所不同,在冒烟测试阶段,CR着重检查开关、接口名、接口参数、model字段;在功能测试阶段,CR重点关注接口异常/跳转、行为;回归阶段则优先检查Bug Fix的代码改动。测试阶段均通过行差异覆盖率检查测试遗漏,并反馈更新测试策略。

图13: 特卖会App精准测试策略

 下一步计划 

目前使用的人工分析+工具辅助的方式进行精准测试,在这条探索的道路上我们还有很长的路要走。人工分析受制于技术,需求背景,能力和经验,我们很难保证质量。如何把已有的覆盖率数据转化为事前分析的参考,并结合整理的用例-模块(文件、函数、分支)映射库实行自助分析、测试用例集推荐,将是我们下一步研究的重点。

分享给小伙伴们:
本文标签: 唯品会代码覆盖率

相关文章

发表评论愿您的每句评论,都能给大家的生活添色彩,带来共鸣,带来思索,带来快乐。

CopyRight © 2015-2016 QingPingShan.com , All Rights Reserved.

清屏网 版权所有 豫ICP备15026204号