集成测试的覆盖率分析支持

目录

概述
为覆盖率分析构建二进制文件
运行覆盖率分析二进制文件
使用覆盖率数据文件
常见问题解答
资源
词汇表

从 Go 1.20 开始,Go 支持从应用程序和集成测试(Go 程序的更大、更复杂的测试)收集覆盖率分析数据。

概述

Go 通过“go test -coverprofile=... <pkg_target>”命令提供易于使用的支持,用于在包单元测试级别收集覆盖率分析数据。从 Go 1.20 开始,用户现在可以为较大的 集成测试 收集覆盖率分析数据:执行给定应用程序二进制文件多次运行的更重量级、更复杂的测试。

对于单元测试,收集覆盖率分析数据并生成报告需要两个步骤:一个 go test -coverprofile=... 运行,然后调用 go tool cover {-func,-html} 生成报告。

对于集成测试,需要三个步骤:一个 构建 步骤,一个 运行 步骤(可能涉及从构建步骤多次调用二进制文件),最后是一个 报告 步骤,如下所述。

为覆盖率分析构建二进制文件

要构建用于收集覆盖率分析的应用程序,请在对应用程序二进制目标调用 go build 时传递 -cover 标志。有关 go build -cover 调用示例,请参见 下方 的章节。然后可以使用环境变量设置运行生成的二进制文件以捕获覆盖率分析(请参见 运行 的下一章节)。

如何选择要进行检测的包

在给定的“go build -cover”调用期间,Go 命令将选择主模块中的包进行覆盖率分析;其他输入到构建中的包(go.mod 中列出的依赖项或属于 Go 标准库的包)默认情况下不会包含在内。

例如,这是一个包含主包、本地主模块包 greetings 以及从模块外部导入的一组包(包括 rsc.io/quotefmt)的玩具程序(完整程序链接)。

$ cat go.mod
module mydomain.com

go 1.20

require rsc.io/quote v1.5.2

require (
    golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c // indirect
    rsc.io/sampler v1.3.0 // indirect
)

$ cat myprogram.go
package main

import (
    "fmt"
    "mydomain.com/greetings"
    "rsc.io/quote"
)

func main() {
    fmt.Printf("I say %q and %q\n", quote.Hello(), greetings.Goodbye())
}
$ cat greetings/greetings.go
package greetings

func Goodbye() string {
    return "see ya"
}
$ go build -cover -o myprogram.exe .
$

如果您使用“-cover”命令行标志构建此程序并运行它,则分析中将准确包含两个包:mainmydomain.com/greetings;其他依赖包将被排除在外。

希望对要包含在覆盖率分析中的包有更多控制权的用户可以使用“-coverpkg”标志进行构建。示例

$ go build -cover -o myprogramMorePkgs.exe -coverpkg=io,mydomain.com,rsc.io/quote .
$

在上述构建中,来自 mydomain.com 的主包以及 rsc.io/quoteio 包被选用于分析;由于 mydomain.com/greetings 未被特别列出,因此即使它位于主模块中,也会从分析中排除。

运行覆盖率分析二进制文件

使用“-cover”构建的二进制文件会在执行结束时将分析数据文件写入通过环境变量 GOCOVERDIR 指定的目录。示例

$ go build -cover -o myprogram.exe myprogram.go
$ mkdir somedata
$ GOCOVERDIR=somedata ./myprogram.exe
I say "Hello, world." and "see ya"
$ ls somedata
covcounters.c6de772f99010ef5925877a7b05db4cc.2424989.1670252383678349347
covmeta.c6de772f99010ef5925877a7b05db4cc
$

请注意写入目录 somedata 的两个文件:这些(二进制)文件包含覆盖率分析结果。有关如何从这些数据文件生成人类可读结果的更多信息,请参见 报告 的下一章节。

如果未设置 GOCOVERDIR 环境变量,经过覆盖率检测的二进制文件仍会正确执行,但会发出警告。示例

$ ./myprogram.exe
warning: GOCOVERDIR not set, no coverage data emitted
I say "Hello, world." and "see ya"
$

涉及多次运行的测试

集成测试在许多情况下可能涉及多个程序运行;当使用“-cover”构建程序时,每次运行都会生成一个新的数据文件。示例

$ mkdir somedata2
$ GOCOVERDIR=somedata2 ./myprogram.exe          // first run
I say "Hello, world." and "see ya"
$ GOCOVERDIR=somedata2 ./myprogram.exe -flag    // second run
I say "Hello, world." and "see ya"
$ ls somedata2
covcounters.890814fca98ac3a4d41b9bd2a7ec9f7f.2456041.1670259309405583534
covcounters.890814fca98ac3a4d41b9bd2a7ec9f7f.2456047.1670259309410891043
covmeta.890814fca98ac3a4d41b9bd2a7ec9f7f
$

覆盖率数据输出文件有两种类型:元数据文件(包含从一次运行到另一次运行不变的项目,例如源文件名和函数名)和计数器数据文件(记录执行的程序部分)。

在上面的示例中,第一次运行生成了两个文件(counter 和 meta),而第二次运行只生成了一个 counter 数据文件:因为元数据不会随着运行而改变,所以只需要写入一次。

使用覆盖率数据文件

Go 1.20 引入了一个新工具“covdata”,可用于从 GOCOVERDIR 目录读取和操作覆盖率数据文件。

Go 的 covdata 工具以各种模式运行。covdata 工具调用的常规形式采用以下形式

$ go tool covdata <mode> -i=<dir1,dir2,...> ...flags...

其中“-i”标志提供要读取的目录列表,其中每个目录都派生自覆盖率工具二进制文件的执行(通过 GOCOVERDIR)。

创建覆盖率分析报告

本节讨论如何使用“go tool covdata”从覆盖率数据文件中生成人类可读的报告。

报告已覆盖的百分比语句

要为每个工具包报告“已覆盖的百分比语句”指标,请使用命令“go tool covdata percent -i=<directory>”。使用上面 running 部分中的示例

$ ls somedata
covcounters.c6de772f99010ef5925877a7b05db4cc.2424989.1670252383678349347
covmeta.c6de772f99010ef5925877a7b05db4cc
$ go tool covdata percent -i=somedata
    main    coverage: 100.0% of statements
    mydomain.com/greetings  coverage: 100.0% of statements
$

此处的“已覆盖语句”百分比直接对应于 go test -cover 报告的百分比。

转换为旧文本格式

可以使用 covdata textfmt 选择器将二进制覆盖率数据文件转换为“go test -coverprofile=<outfile>”生成的旧文本格式。然后可以使用“go tool cover -func”或“go tool cover -html”使用结果文本文件创建其他报告。示例

$ ls somedata
covcounters.c6de772f99010ef5925877a7b05db4cc.2424989.1670252383678349347
covmeta.c6de772f99010ef5925877a7b05db4cc
$ go tool covdata textfmt -i=somedata -o profile.txt
$ cat profile.txt
mode: set
mydomain.com/myprogram.go:10.13,12.2 1 1
mydomain.com/greetings/greetings.go:3.23,5.2 1 1
$ go tool cover -func=profile.txt
mydomain.com/greetings/greetings.go:3:  Goodbye     100.0%
mydomain.com/myprogram.go:10:       main        100.0%
total:                  (statements)    100.0%
$

合并

go tool covdata”的 merge 子命令可用于合并来自多个数据目录的分析。

例如,考虑一个在 macOS 和 Windows 上运行的程序。此程序的作者可能希望将每个操作系统上单独运行的覆盖率分析合并到一个分析语料库中,以便生成跨平台覆盖率摘要。例如

$ ls windows_datadir
covcounters.f3833f80c91d8229544b25a855285890.1025623.1667481441036838252
covcounters.f3833f80c91d8229544b25a855285890.1025628.1667481441042785007
covmeta.f3833f80c91d8229544b25a855285890
$ ls macos_datadir
covcounters.b245ad845b5068d116a4e25033b429fb.1025358.1667481440551734165
covcounters.b245ad845b5068d116a4e25033b429fb.1025364.1667481440557770197
covmeta.b245ad845b5068d116a4e25033b429fb
$ ls macos_datadir
$ mkdir merged
$ go tool covdata merge -i=windows_datadir,macos_datadir -o merged
$

上面的合并操作将合并指定输入目录中的数据,并将一组新的合并数据文件写入“merged”目录。

包选择

大多数“go tool covdata”命令都支持“-pkg”标志,以在操作中执行包选择;“-pkg”的参数采用与 Go 命令的“-coverpkg”标志相同形式。示例


$ ls somedata
covcounters.c6de772f99010ef5925877a7b05db4cc.2424989.1670252383678349347
covmeta.c6de772f99010ef5925877a7b05db4cc
$ go tool covdata percent -i=somedata -pkg=mydomain.com/greetings
    mydomain.com/greetings  coverage: 100.0% of statements
$ go tool covdata percent -i=somedata -pkg=nonexistentpackage
$

-pkg” 标志可用于选择给定报告感兴趣的特定软件包子集。

常见问题解答

  1. 如何请求对 go.mod 文件中提到的所有导入软件包进行覆盖率检测?
  2. 是否可以在 GOPATH/GO111MODULE=off 模式下使用 go build -cover
  3. 如果我的程序崩溃,是否会写入覆盖率数据?
  4. -coverpkg=main 是否会选择我的主软件包进行分析?

如何请求对 go.mod 文件中提到的所有导入软件包进行覆盖率检测?

默认情况下,go build -cover 会检测所有主模块软件包的覆盖率,但不会检测主模块之外的导入(例如标准库软件包或 go.mod 中列出的导入)。请求检测所有非 stdlib 依赖项的一种方法是将 go list 的输出馈送到 -coverpkg。以下是一个示例,再次使用上面引用的 示例程序

$ go list -f '{{if not .Standard}}{{.ImportPath}}{{end}}' -deps . | paste -sd "," > pkgs.txt
$ go build -o myprogram.exe -coverpkg=`cat pkgs.txt` .
$ mkdir somedata
$ GOCOVERDIR=somedata ./myprogram.exe
$ go tool covdata percent -i=somedata
    golang.org/x/text/internal/tag  coverage: 78.4% of statements
    golang.org/x/text/language  coverage: 35.5% of statements
    mydomain.com    coverage: 100.0% of statements
    mydomain.com/greetings  coverage: 100.0% of statements
    rsc.io/quote    coverage: 25.0% of statements
    rsc.io/sampler  coverage: 86.7% of statements
$

是否可以在 GO111MODULE=off 模式下使用 go build -cover

是的,go build -cover 确实可以与 GO111MODULE=off 配合使用。在 GO111MODULE=off 模式下构建程序时,只有在命令行上明确指定为目标的软件包才会被检测以进行分析。使用 -coverpkg 标志可在分析中包括其他软件包。

如果我的程序崩溃,是否会写入覆盖率数据?

使用 go build -cover 构建的程序仅当程序调用 os.Exit() 或从 main.main 正常返回时,才会在执行结束时写出完整的分析数据。如果程序以未恢复的崩溃终止,或者程序遇到致命异常(例如分段错误、除以零等),则在运行期间执行的语句的分析数据将丢失。

-coverpkg=main 是否会选择我的主软件包进行分析?

-coverpkg 标志接受导入路径列表,而不是软件包名称列表。如果你想选择 main 软件包进行覆盖率检测,请通过导入路径(而不是名称)来识别它。示例(使用 此示例程序

$ go list -m
mydomain.com
$ go build -coverpkg=main -o oops.exe .
warning: no packages being built depend on matches for pattern main
$ go build -coverpkg=mydomain.com -o myprogram.exe .
$ mkdir somedata
$ GOCOVERDIR=somedata ./myprogram.exe
I say "Hello, world." and "see ya"
$ go tool covdata percent -i=somedata
    mydomain.com    coverage: 100.0% of statements
$

资源

词汇表

单元测试:在与特定 Go 包关联的 *_test.go 文件中进行的测试,利用 Go 的 testing 包。

集成测试:针对给定应用程序或二进制文件进行的更全面、更重量级的测试。集成测试通常涉及构建程序或一组程序,然后使用多个输入和场景运行程序系列,由可能基于或不基于 Go 的 testing 包的测试框架控制。