聊聊Go代码覆盖率技术与最佳实践
"聊点干货"
覆盖率技术基础
截止到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代码覆盖率技术与最佳实践的更多相关文章
- Atitit.log日志技术的最佳实践attilax总结
Atitit.log日志技术的最佳实践attilax总结 1. 日志的意义与作用1 1.1. 日志系统是一种不可或缺的单元测试,跟踪调试工具1 2. 俩种实现[1]日志系统作为一种服务进程存在 [2] ...
- 【大数据和云计算技术社区】分库分表技术演进&最佳实践笔记
1.需求背景 移动互联网时代,海量的用户每天产生海量的数量,这些海量数据远不是一张表能Hold住的.比如 用户表:支付宝8亿,微信10亿.CITIC对公140万,对私8700万. 订单表:美团每天几千 ...
- XPages访问关系型数据库技术与最佳实践
XPage 对于 Domino 开发人员的一大好处就是能够很方便和高效的访问关系型数据库.本文通过实例代码展现了在 XPage 中访问关系型数据库的具体步骤 , 同时讲解了一些在 XPage 中高效访 ...
- Python自动化运维:技术与最佳实践 PDF高清完整版|网盘下载内附地址提取码|
内容简介: <Python自动化运维:技术与最佳实践>一书在中国运维领域将有“划时代”的重要意义:一方面,这是国内第一本从纵.深和实践角度探讨Python在运维领域应用的著作:一方面本书的 ...
- Python自动化运维 技术与最佳实践PDF高清完整版免费下载|百度云盘|Python基础教程免费电子书
点击获取提取码:7bl4 一.内容简介 <python自动化运维:技术与最佳实践>一书在中国运维领域将有"划时代"的重要意义:一方面,这是国内第一本从纵.深和实践角度探 ...
- 分库分表技术演进&最佳实践
每个优秀的程序员和架构师都应该掌握分库分表,这是我的观点. 移动互联网时代,海量的用户每天产生海量的数量,比如: 用户表 订单表 交易流水表 以支付宝用户为例,8亿:微信用户更是10亿.订单表更夸张, ...
- Unity游戏开发技术的最佳实践
活动详情 作为全球规模最大的Unity开发者聚会,历年的Unite大会都成为开发者们获取Unity最新技术知识,交流开发经验,把握行业发展脉搏,体验全球前沿科技与高品质Made with Unit ...
- IT技术方案最佳实践方案的收集
一.图片鉴别服务 1. 阿里云,腾讯云等公开的服务. 2. 图谱科技提供的API 二. 网络直播服务器 1. SRS2 开源服务器 (https://github.com/ossrs/srs/wiki ...
- 《Ansible自动化运维:技术与最佳实践》第三章读书笔记
Ansible 组件介绍 本章主要通过对 Ansible 经常使用的组件进行讲解,使对 Ansible 有一个更全面的了解,主要包含以下内容: Ansible Inventory Ansible Ad ...
随机推荐
- 什么是64位和32位internet explorer
什么是64位和32位internet explorer 如果您使用 64 位版本的 Internet Explorer 时,您会遇到问题,请尝试使用 32 位版本的 Internet Explorer ...
- C++枚举变量与switch
转载:https://www.cnblogs.com/banmei-brandy/p/11263927.html 枚举类型和变量如何定义,下篇博客讲得十分详细: https://blog.csdn.n ...
- Opencv的imread用法
所有参考来自网上仅仅做学习记录用,具体正确性需要在具体项目各自验证,不涉及具体错误代码处理调试等问题,欢迎发现发现问题~ 参考: 1. https://blog.csdn.net/LiheZhu/ar ...
- Oracle 数据库导入数据和编码问题
配置 control 文件: load data characterset utf8 append into table role_res_gold fields terminated by ';' ...
- ubuntu20 使用命令安装 nginx
安装 nginx sudo apt-get install nginx -y 配置文件 nginx 服务管理 # 启动 nginx 服务 service nginx start # 关闭 nginx ...
- Hello World -- 第一篇博客 -- 活着的意义
今年注定是不寻常的一年,因为技术,接触了许多大牛.通过一篇篇博文,看到了大牛们勤奋好学.孜孜不倦的精神,于是决定也开个博客,向大牛学习. 博客开了,写点什么呢?奈何肚子里墨水不多,吐出来也多是白沫,不 ...
- linux块设备驱动---概念与框架(转)
基本概念 块设备(blockdevice) --- 是一种具有一定结构的随机存取设备,对这种设备的读写是按块进行的,他使用缓冲区来存放暂时的数据,待条件成熟后,从缓存一次性写入设备或者从设备一次性 ...
- 编程体系结构(08):Spring.Mvc.Boot框架
本文源码:GitHub·点这里 || GitEE·点这里 一.Spring框架 1.框架概述 Spring是一个开源框架,框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 ...
- 50种编程语言,一句 “Hello, World”!展现编程语言七十年发展!
mod confinment { use std::os::raw::{c_char}; extern "C" { pub fn puts(txt: *const c_char); ...
- 如何使用懒加载 - umi
.umirc.js文件 plugins: [ dynamicImport: true,