"聊点干货"

覆盖率技术基础

截止到Go1.15.2以前,关于覆盖率技术底层实现,以下知识点您应该知道:

  • go语言采用的是插桩源码的形式,而不是待二进制执行时再去设置breakpoints。这就导致了当前go的测试覆盖率收集技术,一定是侵入式的,会修改目标程序源码。曾经有同学会问,插过桩的二进制能不能放到线上,所以建议最好不要。

  • 到底什么是"插桩"?这个问题很关键。大家可以任意找一个go文件,试试命令go tool cover -mode=count -var=CoverageVariableName xxxx.go,看看输出的文件是什么?

    • 笔者以这个文件为例https://github.com/qiniu/goc/blob/master/goc.go, 得到以下结果:

      	package main
      
      	import "github.com/qiniu/goc/cmd"
      
      	func main() {CoverageVariableName.Count[0]++;
      cmd.Execute()
      } var CoverageVariableName = struct {
      Count [1]uint32
      Pos [3 * 1]uint32
      NumStmt [1]uint16
      } {
      Pos: [3 * 1]uint32{
      21, 23, 0x2000d, // [0]
      },
      NumStmt: [1]uint16{
      1, // 0
      },
      }

可以看到,执行完之后,源码里多了个CoverageVariableName变量,其有三个比较关键的属性:

* `Count` uint32数组,数组中每个元素代表相应基本块(basic block)被执行到的次数
* `Pos` 代表的各个基本块在源码文件中的位置,三个为一组。比如这里的`21`代表该基本块的起始行数,`23`代表结束行数,`0x2000d`比较有趣,其前16位代表结束列数,后16位代表起始列数。通过行和列能唯一确定一个点,而通过起始点和结束点,就能精确表达某基本块在源码文件中的物理范围
* `NumStmt` 代表相应基本块范围内有多少语句(statement) `CoverageVariableName`变量会在每个执行逻辑单元设置个计数器,比如`CoverageVariableName.Count[0]++`, 而这就是所谓插桩了。通过这个计数器能很方便的计算出这块代码是否被执行到,以及执行了多少次。相信大家一定见过表示go覆盖率结果的coverprofile数据,类似下面:
`github.com/qiniu/goc/goc.go:21.13,23.2 1 1` 这里的内容就是通过类似上面的变量`CoverageVariableName`得到。其基本语义为
"**文件:起始行.起始列,结束行.结束列 该基本块中的语句数量 该基本块被执行到的次数**"

依托于go语言官方强大的工具链,大家可以非常方便的做单测覆盖率收集与统计。但是集测/E2E就不是那么方便了。不过好在我们现在有了https://github.com/qiniu/goc。

集测覆盖率收集利器 - Goc原理

关于单测这块,深入go源码,我们会发现go test -cover命令会自动生成一个_testmain.go 文件。这个文件会Import各个插过桩的包,这样就可以直接读取插桩变量,从而计算测试覆盖率。实际上goc也是类似的原理(PS: 关于为何不直接用go test -c -cover 方案,可以参考这里https://mp.weixin.qq.com/s/DzXEXwepaouSuD2dPVloOg)。

不过集测时,被测对象通常是完整产品,涉及到多个long running的后端服务。所以goc在设计上会自动化会给每个服务注入HTTP API,同时通过服务注册中心goc server来管理所有被测服务。如此的话,就可以在运行时,通过命令goc profile实时获取整个集群的覆盖率结果,当真非常方便。

整体架构参见:

代码覆盖率的最佳实践

技术需要为企业价值服务,不然就是在耍流氓。可以看到,目前玩覆盖率的,主要有以下几个方向:

  • 度量 - 深度度量,各种包,文件,方法度量,都属于该体系。其背后的价值在于反馈与发现。反馈测试水平如何,发现不足或风险并予以提高。比如常见的作为流水线准入标准,发布门禁等等。度量是基础,但不能止步于数据。覆盖率的终极目标,是提高测试覆盖率,尤其是自动化场景的覆盖率,并一以贯之。所以基于此,业界我们看到,做的比较有价值的落地形态是增量覆盖率的度量。goc diff 结合Prow平台也落地了类似的能力,如果您内部也使用Kubernetes,不妨尝试一下。当然同类型的比较知名的商业化服务,也有CodeCov/Coveralls等,不过目前她们多数是局限在单测领域。

  • 精准测试方向 - 这是个很大的方向,其背后的价值逻辑比较清晰,就是建立业务到代码的双向反馈,用于提升测试行为的精准高效。但这里其实含有悖论,懂代码的同学,大概率不需要无脑反馈;不能深入到代码的同学,你给代码级别的反馈,也效果不大。所以这里落地姿势很重要。目前业界没还看到有比较好的实践例子,大部分都是解决特定场景下的问题,有一定的局限。

而相较于落地方向,作为广大研发同学,下面这些最佳实践可能对您更有价值:

  • 高代码覆盖率并不能保证高产品质量,但低代码覆盖率一定说明大部分逻辑没有被自动化测到。后者通常会增加问题遗留到线上的风险,当引起注意。
  • 没有普适的针对所有产品的严格覆盖率标准。实际上这更应该是业务或技术负责人基于自己的领域知识,代码模块的重要程度,修改频率等等因素,自行在团队中确定标准,并推动成为团队共识。
  • 低代码覆盖率并不可怕,能够主动去分析未被覆盖到的部分,并评估风险是否可接受,会更加有意义。实际上笔者认为,只要这一次的提交比上一次要好,都是值得鼓励的。

谷歌有篇博客(参考资料)提到,其经验表明,重视代码覆盖率的团队通常会更加容易培养卓越工程师文化,因为这些团队在设计产品之初就会考虑可测性问题,以便能更轻松的实现测试目标。而这些措施反过来会促使工程师编写更高质量的代码,更注重模块化。

最后,欢迎点击左下角详情按钮,加入七牛云Goc交流群,我们一起聊聊goc,聊聊研发效能那些事。

参考资料

往期推荐

觉得不错,欢迎关注:

聊聊Go代码覆盖率技术与最佳实践的更多相关文章

  1. Atitit.log日志技术的最佳实践attilax总结

    Atitit.log日志技术的最佳实践attilax总结 1. 日志的意义与作用1 1.1. 日志系统是一种不可或缺的单元测试,跟踪调试工具1 2. 俩种实现[1]日志系统作为一种服务进程存在 [2] ...

  2. 【大数据和云计算技术社区】分库分表技术演进&最佳实践笔记

    1.需求背景 移动互联网时代,海量的用户每天产生海量的数量,这些海量数据远不是一张表能Hold住的.比如 用户表:支付宝8亿,微信10亿.CITIC对公140万,对私8700万. 订单表:美团每天几千 ...

  3. XPages访问关系型数据库技术与最佳实践

    XPage 对于 Domino 开发人员的一大好处就是能够很方便和高效的访问关系型数据库.本文通过实例代码展现了在 XPage 中访问关系型数据库的具体步骤 , 同时讲解了一些在 XPage 中高效访 ...

  4. Python自动化运维:技术与最佳实践 PDF高清完整版|网盘下载内附地址提取码|

    内容简介: <Python自动化运维:技术与最佳实践>一书在中国运维领域将有“划时代”的重要意义:一方面,这是国内第一本从纵.深和实践角度探讨Python在运维领域应用的著作:一方面本书的 ...

  5. Python自动化运维 技术与最佳实践PDF高清完整版免费下载|百度云盘|Python基础教程免费电子书

    点击获取提取码:7bl4 一.内容简介 <python自动化运维:技术与最佳实践>一书在中国运维领域将有"划时代"的重要意义:一方面,这是国内第一本从纵.深和实践角度探 ...

  6. 分库分表技术演进&最佳实践

    每个优秀的程序员和架构师都应该掌握分库分表,这是我的观点. 移动互联网时代,海量的用户每天产生海量的数量,比如: 用户表 订单表 交易流水表 以支付宝用户为例,8亿:微信用户更是10亿.订单表更夸张, ...

  7. Unity游戏开发技术的最佳实践

    活动详情   作为全球规模最大的Unity开发者聚会,历年的Unite大会都成为开发者们获取Unity最新技术知识,交流开发经验,把握行业发展脉搏,体验全球前沿科技与高品质Made with Unit ...

  8. IT技术方案最佳实践方案的收集

    一.图片鉴别服务 1. 阿里云,腾讯云等公开的服务. 2. 图谱科技提供的API 二. 网络直播服务器 1. SRS2 开源服务器 (https://github.com/ossrs/srs/wiki ...

  9. 《Ansible自动化运维:技术与最佳实践》第三章读书笔记

    Ansible 组件介绍 本章主要通过对 Ansible 经常使用的组件进行讲解,使对 Ansible 有一个更全面的了解,主要包含以下内容: Ansible Inventory Ansible Ad ...

随机推荐

  1. 手写“SpringBoot”近况:IoC模块已经完成

    jsoncat:https://github.com/Snailclimb/jsoncat (About 仿 Spring Boot 但不同于 Spring Boot 的一个轻量级的 HTTP 框架) ...

  2. Python如何安装OpenCV库

    转载:https://blog.csdn.net/weixin_35684521/article/details/81953047 OpenCV的概念可百度,在此不再赘述.https://baike. ...

  3. JS/TS 对数组中的对象按对象的值进行去重

    举个例子:对以下数组按 lastName 的值进行去重 let listData = [ { firstName: "Rick", lastName: "Sanchez& ...

  4. Nuxt/Vue自定义导航栏Topbar+标签栏Tabbar组件

    基于Vue.js实现自定义Topbar+Tabbar组件|仿咸鱼底部凸起导航 最近一直在倒腾Nuxt项目,由于Nuxt.js是基于Vue.js的服务端渲染框架,只要是会vue,基本能很快上手了. 一般 ...

  5. .net core中的那些常用的日志框架(NLog篇)

    前言 咱们上回讲到,.net core中内置的Logging日志框架的使用,以及浅显的讲解,接下来,给大家介绍一个第三方日志框架(NLog). NLog简介 NLog是适用于各种.NET平台(包括.N ...

  6. [Docker] redis 全配置

    启动容器,加载配置文件并持久化数据 docker run -d --privileged=true -p 6379:6379 --restart always -v /usr/redis/conf:/ ...

  7. k8s集群调度方案

    Scheduler是k8s集群的调度器,主要的任务是把定义好的pod分配到集群节点上 有以下特征: 1  公平   保证每一个节点都能被合理分配资源或者能被分配资源 2  资源高效利用   集群所有资 ...

  8. Flask实现websocket

    from flask import Flask,request user_socket_dict = {} app = Flask(__name__) @app.route("/conn_w ...

  9. day31 Pyhton 面向对象的基础 三大特性

    一.内容回顾 封装 1.概念 笔记 2.__名字 在类的外部就不能用了 3.私有化的 不能被子类继承,也不能在其他任何类中调用 三个装饰器方法(装饰类中的方法) 1.不被修饰的  普通方法,会使用对象 ...

  10. 联赛模拟测试17 A. 简单的区间 启发式合并

    题目描述 分析 我们要找的是一段区间的和减去该区间的最大值能否被 \(k\) 整除 那么对于一段区间,我们可以先找出区间中的最大值 然后枚举最大值左边的后缀与最大值右边的前缀之和是否能被 \(k\) ...