写点什么

有赞 GO 项目单测、集成、增量覆盖率统计与分析

  • 2020-03-31
  • 本文字数:4968 字

    阅读完需:约 16 分钟

有赞 GO 项目单测、集成、增量覆盖率统计与分析

一、引言

我是一名中间件 QA,我对应的研发团队是有赞 PaaS,目前我们团队有很多产品是使用 go 语言开发,因此我对 go 语言项目的单测覆盖率、集成以及增量测试覆盖率统计与分析做了探索。

二、单测覆盖率以及静态代码分析

2.1 单测覆盖率分析

Go 语言自身提供了单元测试工具 go test,单元测试文件必须以 *_test.go 形式存在, go test 工具同时也提供了分析单测覆盖率的功能。因为需要将单测覆盖率上传到 sonar 平台展示,所以必须将覆盖率文件转换成能被 sonar 识别的格式,因此,还需要另外一个命令行工具 gocov。首先我们使用 go test 生成覆盖率输出文件 cover.out,并通过 gocov 工具来将生成的覆盖率文件 cover.out 转换成可以被 sonar 识别的 Cobertura 格式的 xml 文件。如下所示:


go test -v ./... -coverprofile=cover.out #生成覆盖率输出gocov convert cover.out | gocov-xml > coverage.xml #将覆盖率输出转换成xml格式的报告
复制代码


将生成的单测覆盖率报告发送到 sonar 平台上来展示。

2.2 静态代码分析

Go 静态代码分析工具有两个,分别是 gometalinter 和 golangci-lint,我们现在使用的是 golangci-lint,因为 gometalinter 已经停止维护,而且作者也推荐去使用 golangci-lint

2.2.1 golangci-lint 的安装

以下是安装 golangci-lint 推荐的两种方法:


  • 将二进制文件安装在 (go env GOPATH)/bin/golangci-lint 目录下 curl-sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin vX.Y.Z

  • 或者将二进制文件安装在 ./bin/ 目录下 curl-sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s vX.Y.Z


安装完成之后可以通过使用 golangci-lint--version来查看它的版本。

2.2.2 golangci-lint 的使用

在需要进行静态代码扫描的目录下执行 golangci-lint run,此命令和 golangci-lint run./… 命令等效,表示扫描整个项目文件代码,并进行监测,也可以通过指定 go 文件或者文件目录名来对特定的代码文件或者目录进行代码扫描,例如 golangci-lint run dir1 dir2/...dir3/file1.go


ps:扫描指定目录的时候是不支持递归扫描的,如果要进行递归扫描需要在目录路径后面追加 /…


默认情况下 golangci-lint 只启用以下的 linters:


Enabled by default linters:


deadcode: 发现没有使用的代码


  • errcheck: 用于检查 go 程序中有 error 返回的函数,却没有做判断检查

  • gosimple: 检测代码是否可以简化

  • govet (vet, vetshadow): 检查 go 源代码并报告可疑结构,例如 Printf 调用,其参数与格式字符串不一致

  • ineffassign: 检测是否有未使用的代码、变量、常量、类型、结构体、函数、函数参数等

  • staticcheck: 提供了巨多的静态检查,检查 bug,分析性能等

  • structcheck:发现未使用的结构体字段

  • typecheck: 对 go 代码进行解析和类型检查

  • unused: 检查未使用的常量,变量,函数和类型

  • varcheck: 查找未使用的全局变量和常量


Disabled by default linters:


bodyclose: 对 HTTP 响应是否 close 成功检测


  • dupl: 代码克隆监测工具

  • gochecknoglobals: 检查 go 代码中是否存在全局变量

  • goimports: 做所有 gofmt 做的事. 此外还检查未使用的导入

  • golint: 打印出 go 代码的格式错误

  • gofmt: 检测代码是否都已经格式化, 默认情况下使用 -s 来检查代码是否简化

  • ……………………………


未启用的还有很多工具,可以通过使用 golangci-lint help linters命令查看还有哪些工具可以使用,如果想要启用没有默认开启的工具,可以在执行命令时使用 -E 参数来启用,比如要启用 golint 的话,只需要执行一下命令 golangci-lint run-E=golint。除了用 -E 来启动参数外,还可以指定最长执行时间 —deadline、跳过要扫描的目录 --skip-dirs 等等。如果要了解更多,请使用 golangci-lint run-h来查看。


特别注意 —-exclude-use-default 参数,golangci-lint 对于上面默认的启用 linters 中做了一些过滤措施,比如对于 errcheck ,它不会扫描 ((os\.)?std(out|err)\..*|.*Close|.*Flush|os\.Remove(All)?|.*printf?|os\.(Un)?Setenv) 这些函数返回的 error 是否被 checked,所以如果代码中使用到这些函数,并且没有接收 error 的话是不会被扫描到的。类似的还有golintgovetstaticcheckgosec需要注意。如果想要不过滤这些就需要使用 --exclude-use-default=false来启用。

2.3 接入 sonar

go 接入 sonar 需要 sonar-scanner 工具以及 sonar-project.properties 文件。

2.3.1 sonar-scanner

sonar-scanner 是 sonar 官方提供的代码扫描器,下载地址是 https://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner。下载好之后解压,解压后的目录下有四个文件夹,分别是 bin、conf、jre、lib,然后将 bin 文件夹路径添加到 $PATH 环境变量下,使用 sonar-scanner-v 来查看版本。

2.3.2 sonar-project.properties

sonar-project.properties 文件的作用主要是配置 sonar 扫描器扫描哪些类型的文件以及文件目录,最后将报表结果上报到 sonar 服务器,sonar-project.propertie 内容如下:


内容如下:


#sonar安装的服务器地址sonar.host.url=http://ip:port#服务器账号sonar.login=root#服务器密码sonar.password=root#项目使用的语言sonar.language=go#项目的独特关键字,maven 项目是 <groupId>:<artiactId>,go 项目自己定义就可以sonar.projectKey=projectKey#将在web界面上显示的名字sonar.projectName=demo#项目版本sonar.projectVersion=1.0#需要分析的源码目录的路径sonar.sources=.sonar.exclusions=**/*_test.go,**/vendor/**sonar.tests=.sonar.test.inclusions=**/*_test.gosonar.test.exclusions=**/vendor/**#golangci-lint 报告路径sonar.go.golangci-lint.reportPaths=report.xml#单测覆盖率报告地址sonar.go.coverage.reportPaths=cover.out
复制代码


在项目目录下分别执行go test-v./...-coverprofile=cover.out以及 golangci-lint run--out-format checkstyle./...&gt;report.xml等生产报告,并执行 sonar-scan 来将生成的报告上传到服务器。这里默认在使用的是 sonar8.1 已经支持了 golangci-lint报告主页


三、集成测试覆盖率分析

对于 Go 项目没有类似 java jacoco 这样的第三方测试工具,就算是开源的第三方工具,一般单元测试执行以及单测覆盖率分析都是使用 Go 自带的测试工具 go test 来执行的。


阅读了 GO 的官方博客之后发现其实针对二进制文件是有类似的工具 gcov。在文章中作者也说了,对于在 go 1.2 之前,其实也是使用类似 gcov 的方式对二进制程序在分支上设置断点,在每个分支执行时,将断点清除并将分支的目标语句标记为 “covered” 。


但是通过文章可以知道,在 go 1.2 之后是不支持使用此种方式,而且也不推荐使用 gcov 来统计覆盖率,因为执行二进制分析是很有挑战且很困难的,它还需要一种可靠的方式来执行跟踪绑定到源代码,这也很困难,这些问题包括不准确的调试信息和类似内联函数使分析复杂化,最重要的是,这种方法非常不便携。

3.1 解决方法

通过查找资料,发现了一个并不完美但是可以解决这个问题的方法。go test 中有一个 -c 的 flag,可以将单测的代码和被单测调用的代码编译成二进制包执行,但是这种方式并没有将整个项目的代码包含进去,不过可以通过增加一个测试文件 main_test.go,文件内容如下:


func TestMainStart(t *testing.T) {    var args []string    for _, arg := range os.Args {        if !strings.HasPrefix(arg, "-test") {            args = append(args, arg)        }    }    os.Args = args    main()}
复制代码


将主函数放在此测试代码中,由于 Go 的入口函数是 main 函数,所以这样就会将整个 Go 项目都打包成一个已经插桩的二进制文件,如果项目启动的时候需要传入参数,则会将其中程序启动时传入的不是 -test 标记的参数放入到 os.Args 中传递给 main 函数。以上代码也可以自己在测试文件中增加消息通知监听,来退出测试函数。当集成测试跑完后就可以得到覆盖率代码,整个流程可参考下图:



#第一步:执行集成测试,并将此函数编译成二进制文件go test -coverpkg="./..." -c -o cover.test#第二步:运行二进制文件,指定运行的测试方法是 TestMainStart,并将覆盖率报告输出./cover.test -test.run "TestMainStart" -test.coverprofile=cover.out#第三步:将输出的覆盖率报告转换成 html 文件(html 文件查看效果比较好)go tool cover -html cover.out -o cover.html#第四步:生成 Cobertura 格式的 xml 文件gocov convert cover.out | gocov-xml > cover.xml
复制代码

3.2 缺点

  1. 必须所有 Go 语言项目中新增一个这样的测试代码文件,才可以使用

  2. 必须退出进程才可以获得报告,但是如果测试程序是在 k8s 的 pod 中,一旦程序退出,pod 就会自动退出无法获取到文件

  3. 想要得到测试覆盖率数据不能像 jacoco 那样直接调用接口可以 dump 到本地,程序必须增加一个接收信号量的参数,保证主函数的退出,不然集成测试代码跑完,覆盖率信息是不会写到磁盘的

  4. 由于上面的原因,报告储存在远端,无法下载到当前 Jenkins 上,要去远端 dump 文件下来分析

  5. 不能将分布式的应用的数据结合起来之后做全量统计(只能跑单个应用)


以上缺陷在有赞 paas 团队通过一些不是特别优雅的方式解决,以下是解决方案

3.3 优化

ps:由于当前有赞 PaaS 的 ci 环境是在 k8s 集群中实现的,所以这里就针对 k8s 中 的优化方案

3.3.1 针对编译前需要新增一个测试文件,包裹 main 函数

测试函数也是要求所有项目中增加一个测试文件,或者 Jenkins 编译部署镜像之前在 pipline 中生成一个文件

3.3.2 针对以上必须程序退出才可以或许到测试覆盖率报告的缺点:

假设 k8s 基础镜像中已经装好 python,我在启动 pod 的时候默认启动两个服务,一个是被测试的服务,一个是 python 启动的 http 服务。


然后将项目服务的启动写入脚本中,并在 deployment 中通过 nohup 启动服务,并再启动一个 python 服务


    spec:      containers:      - command:        - /bin/bash        - -c        - (nohup /data/project/start.sh &);(cd python && -m SimpleHTTPServer 12345)        image: $imageAddress
复制代码


杀死项目服务后,因为还有 python 服务在,pod 不会退出,可以拿到覆盖率测试报告

3.3.3 覆盖率报告在远端,如何在跑完 Jenkins 任务后来直接获取到报告:

可以在跑集成测试后通过执行 http 请求来获取容器内的 cover.out,比如 wget http://{ip}:{port}/{path}/cover.out,并将此覆盖率报告编译成 Cobertura 格式的 xml,放入到 Jenkins 中统计。


如果是执行了多个服务端,需要合并覆盖率报告,可以使用 gocovmerge

3.3.4 如何在 k8s 中自动化 kill 程序让其退出:

对于退出程序可以直接在集成测试代码中使用 kubectl 命令将 pod 中的程序 kill


pid=`kubectl exec $podname -c $container -n dts -- ps -ef | grep $process | grep -v grep | awk '{print $2}'`kubectl exec $podname -c $container -n $namespace -- kill $pid
复制代码

3.4 jenkins 报告

四、集成测试增量覆盖率分析

4.1 diff_cover

增量覆盖率分析我们选择了开源工具 diffover,diffcover 是用 python 开发,通过 git diff 来对比当前分支和需要比对的分支,主要针对新增代码做覆盖率分析。

4.2 安装

安装 diff_cover 的机器需要有 python 的环境,有两种安装方式:


1、通过 pip 来直接下载安装


pip install diff_cover


2、通过源代码安装


pip install diff_covers

4.3 使用方式

ps:必须在需要对比的项目目录下运行!!!

4.3.1 生成单元测试覆盖率报告

go test-v./...-coverprofile=cover.outgocov convert cover.out|gocov-xml&gt;coverage.xml

4.3.2 增量覆盖率分析

diff-cover coverage.xml--compare-branch=xxxx--html-report report.html


–compare-branch:是选择需要对比的分支号

–html-report:是将增量测试报告生成 html 的报告模式

除了以上参数,此工具还有很多其他参数,比如

–fail-under:覆盖率低于某个值,返回非零状态代码

–diff-range-notation:设置 diff 的范围,就是 git diff{compare-branch}{diff-range-notation} 的作用等等。

具体可以通过 diff_cover-h 来获得更多详细的信息

4.4 报告

  1. 命令行展示



  1. HTML 展示



本文转载自公众号有赞 coder(ID:youzan_coder)。


原文链接


https://mp.weixin.qq.com/s/mNGkMggkkuRSuflHw3vIyA


2020-03-31 10:135855

评论

发布
暂无评论
发现更多内容

百度智能云携手面壁智能,深化大模型端云协同合作

极客天地

OmniGraffle Pro:绘图巅峰,设计卓越!

Rose

One Switch for Mac(系统功能快速开关工具) v1.33.1中文版

Rose

老好人无法成为好的管理者

老张

团队管理 技术管理 绩效管理

指标平台在企业数据管理中的定位及其如何与BI、数仓的协同工作?

Aloudata

数据分析 指标体系 指标平台 指标开发

如何在汽车中构建一个时序数据库 (TSDB)?

Greptime 格睿科技

边缘计算 时序数据库 新能源汽车

PDF如何一键转为PPT?10个好用的格式转换工具汇总!

职场工具箱

效率 效率工具 PPT 办公软件 AI生成PPT

Tampermonkey for Mac(油猴Safari浏览器插件)功能介绍

Rose

能操控电脑的 Computer Use 究竟是什么?万能胶水、旧世界操作员,还是无所不在的智能?| 播客《编码人声》

声网

ARB链挖矿DApp系统开发模式定制

区块链软件开发推广运营

交易所开发 dapp开发 链游开发 公链开发 代币开发

跨平台的键鼠共享工具 synergy mac 中文激活版

Rose

BOE(京东方)2024年前三季度净利润三位数增长 “屏之物联”引领企业高质发展

科技热闻

网易伏羲:智能体驱动 未来可期 | 《天堂硅谷》杂志报道

网易伏羲

AI 网易伏羲 AI 人工智能

App Cleaner & Uninstaller Pro for Mac(苹果应用程序清理卸载软件)

Rose

MindNode,一键开启思维整理新模式!

Rose

鸿蒙网络编程系列41-仓颉版HttpRequest模拟登录示例

长弓三石

DevEco Studio 开发实例 HarmonyOS NEXT 网络与连接

从数据提取到管理:合合信息的智能文档处理全方位解析【合合信息智能文档处理百宝箱】

申公豹

人工智能

Serverless + AI 让应用开发更简单

阿里巴巴云原生

阿里云 Serverless 云原生

Sound Control for Mac 强大的音量控制软件

Rose

Topaz Gigapixel AI破解版下载 Topaz Gigapixel AI安装包分享

Rose

AI校园新星直通车再启动:Zilliz助您踏上开源舞台

Zilliz

AI 开源社区 Milvus Zilliz

CAD迷你看图 Mac破解版 v4.4.5免激活版

Rose

总计 30 万奖金,Spring AI Alibaba 应用框架挑战赛开赛

阿里巴巴云原生

阿里云 开源 云原生

浅谈指标平台的价值:赋能企业决策、加速业务响应与提升技术效率

Aloudata

数据仓库 数据分析 指标平台

最大程度降低“去O”的迁移风险

NineData

数据库 复制 迁移 同步 NineData

BOE(京东方)全新一代发光器件赋能iQOO 13 全面引领柔性显示行业性能新高度

爱极客侠

ElevenLabs Voice Design:文本生成个性化语音;科学家用 AI 解读猪叫声背后情绪和压力丨RTE 开发者日报

声网

Alfred 5中文安装包 Mac 上的效率瑰宝!

Rose

有赞 GO 项目单测、集成、增量覆盖率统计与分析_软件工程_赵海强_InfoQ精选文章