有赞团队之GO项目的单测、集成、增量覆盖率统计与分析(二)

发表于:2021-9-07 09:47

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:赵海强    来源:有赞技术

  三、集成测试覆盖率分析
  对于 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 缺点
  必须所有 Go 语言项目中新增一个这样的测试代码文件,才可以使用。
  必须退出进程才可以获得报告,但是如果测试程序是在 k8s 的 pod 中,一旦程序退出,pod 就会自动退出无法获取到文件。
  想要得到测试覆盖率数据不能像 jacoco 那样直接调用接口可以 dump 到本地,程序必须增加一个接收信号量的参数,保证主函数的退出,不然集成测试代码跑完,覆盖率信息是不会写到磁盘的。
  由于上面的原因,报告储存在远端,无法下载到当前 Jenkins 上,要去远端 dump 文件下来分析。
  不能将分布式的应用的数据结合起来之后做全量统计(只能跑单个应用)。
  以上缺陷在有赞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 报告

  本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号