如何计算Go代码单元测试增量覆盖率

2024-11-17T22:34:28+08:00 | 4分钟阅读

bestzy
如何计算Go代码单元测试增量覆盖率

文章讨论了如何使用 Go 语言进行单元测试和增量覆盖率分析,并介绍了如何提高代码覆盖率的效率。

前言

团队中要求提交代码的增量覆盖率要达某阈值后才能合入代码,但每次使用CI检测很麻烦。由此,我在本地搭建出了一套测试环境,来帮助我确认是否达到阈值,以及查看单侧覆盖了哪些代码。

Go test工具

在Go语言中,单元测试文件必须以 *_test.go 形式存在,而Go提供了单元测试工具go test,执行以下命令可以运行项目目录中的所有单元测试。

go test ./...

查看覆盖率

go test 工具同时也提供了分析单测覆盖率的功能,当我们使用如下命令进行时。

go test -cover ./...

控制台将输出每一个package的单侧覆盖率。当然,如果我们想将覆盖率以文件的形式输出,我们可以执行以下命令。

go test -coverprofile=c.out ./...

文件将被命名为c.out,保存至运行命令的目录中。

生成覆盖率报告

我们可以使用go提供的工具go tool来生成覆盖率报告,需要用到上述c.out文件。

go tool cover -html=c.out -o coverage.html

该命令将生成一个名为coverage.html的HTML文件,我们可以在浏览器中打开该文件来查看代码覆盖率报告。

但到此为止,我们仍然无法获知增量覆盖率是多少。即使覆盖率已经达到了90%,也不能保证新增的代码被覆盖其中。

工具准备

diff_cover

diffcover是用python开发的工具,通过 git diff 来对比当前分支和需要比对的分支,主要针对新增代码做覆盖率分析。支持读取 Cobertura 格式的 XML 文件作为输入。

  graph LR
    A[代码测试] --> B[覆盖率数据]
    B --> C[Cobertura XML]
    C --> D[diff-cover-report]
    E[Git Diff] --> D
pip install diff_cover

gocov

gocov是一个Go语言的代码覆盖率分析工具。它的主要功能包括:

  1. 收集Go代码的测试覆盖率数据
  2. 将覆盖率数据转换成多种格式用于展示和分析
  3. 生成覆盖率报告

与go原生工具相比,gocov提供更丰富的输出格式和更详细的报告。

go install github.com/axw/gocov/gocov@latest

gocov-xml

gocov-xml是一个简单的辅助工具,用于从gocov输出中生成Cobertura格式的XML文件。

go install github.com/AlekSi/gocov-xml@latest

工作步骤

为了生成单元测试的增量覆盖率,我们需要先使用go test 命令生成覆盖率报告,再使用gocov工具将覆盖率报告转换为json形式,再使用gocov-xml工具将json形式的报告转换为xml,最后使用diff_cover分析xml文件,得出增量覆盖率,如下图所示。

暂时无法在飞书文档外展示此内容

具体步骤如下:

  1. 执行 go test -coverprofile=out/c.out ./...,将覆盖率报告c.out输出至./out文件夹中
  2. 执行 gocov convert out/c.out | gocov-xml > out/c.xml,将c.out转换为c.xml
  3. 执行 diff-cover out/c.xml --compare-branch=master --html-report=out/c.html --fail-under=60 ,将当前分支与mater分支对比,要求增量覆盖率达到60%,并输出报告的html文件。

举例

假设代码正处于git master分支上,我们在animal包下面实现了如下函数:

package animal

func IsAnimal(name string) bool {
    switch name {
    case "dog":
       return true
    case "cat":
       return true
    default:
       return false
    }
}

并为此编写了单元测试。

func TestIsAnimal(t *testing.T) {
    type args struct {
       name string
    }
    tests := []struct {
       name string
       args args
       want bool
    }{
       {
          name: "case 1",
          args: args{
             name: "dog",
          },
          want: true,
       },
       {
          name: "case 2",
          args: args{
             name: "cat",
          },
          want: true,
       },
       {
          name: "case 3",
          args: args{
             name: "none",
          },
          want: false,
       },
    }
    for _, tt := range tests {
       t.Run(tt.name, func(t *testing.T) {
          if got := IsAnimal(tt.args.name); got != tt.want {
             t.Errorf("IsAnimal() = %v, want %v", got, tt.want)
          }
       })
    }
}

很明显,该单元测试能够100%覆盖master的代码,为了确定能否100%覆盖,我们执行go test -v -cover ./...命令,结果如下:

coverage: 100.0% of statements
ok      cov/animal      0.002s  coverage: 100.0% of statements

现在,我们新增一行代码,但并不扩充单元测试。

package animal

func IsAnimal(name string) bool {
    switch name {
    case "dog":
       return true
    case "cat":
       return true
    case "rabbit":
       return true
    default:
       return false
    }
}

再次执行go test -v -cover ./...,此时单侧覆盖率为80%。

ok      cov/animal      (cached)        coverage: 80.0% of statements

根据流程执行命令,我们得到增量覆盖率为0%:

-------------
animal/check.go (0.0%): Missing lines 10
-------------
Total:   1 line
Missing: 1 line
Coverage: 0%
-------------

提高效率

工作中,我们可以使用make工具帮助我们提高效率。

首先我们先检查环境中是否已经安装了以上工具。

check_tools:
        @command -v gocov > /dev/null 2>&1 || (echo 'goconv is required';exit 1)
        @command -v gocov-xml >/dev/null 2>&1 || (echo 'gocov-xml is required'; exit 1)
        @command -v diff-cover >/dev/null 2>&1 || (echl 'diff-cover is required'; exit 1)

然后运行单元测试并输出增量覆盖率。

# 在外部定义变量
compare_branch := master #对比分支
fail_under := 60 #最低覆盖率要求
out_file := out/coverprofile.out #原覆盖率报告
out_xml := out/coverage.xml #覆盖率xml报告
out_report := out/report.html #增量覆盖率html报告

cover: check_tools
        @go test -coverprofile=$(out_file) ./...
        @gocov convert $(out_file) | gocov-xml > $(out_xml)
        @diff-cover $(out_xml) --compare-branch=$(compare_branch) --html-report=$(out_report) --fail-under=$(fail_under)

那么,当我们每次需要进行测试时,只需要输入以下命令即可。

make cover

仍然使用上述例子,执行之后运行结果如下:

root@LAPTOP-QRJSTFAO:~/cov# make cover 
        cov             coverage: 0.0% of statements
ok      cov/animal      0.002s  coverage: 80.0% of statements
gocov convert out/coverprofile.out  | gocov-xml > out/coverage.xml
diff-cover out/coverage.xml --compare-branch=master  --html-report=out/report.html --fail-under=60 
Failure. Coverage is below 60%.
-------------
Diff Coverage
Diff: master...HEAD, staged and unstaged changes
-------------
animal/check.go (0.0%): Missing lines 10
-------------
Total:   1 line
Missing: 1 line
Coverage: 0%
-------------

© 2025 Bestzy's Blog

🌱 Powered by Hugo with theme Dream.

About Me

👋 Hi, This is Zheng Yi.