代码覆盖工具可帮助开发人员确定在执行给定测试套件时执行(覆盖)了源代码库的哪一部分。
一段时间以来,Go 提供了支持(在 Go 1.2 版本中引入)来测量包级别的代码覆盖率,使用“go test”命令的“-cover”标志。
该工具在大多数情况下都运行良好,但对于较大的 Go 应用程序有一些弱点。
对于此类应用程序,开发人员通常会编写“集成”测试来验证整个程序的行为(除了包级单元测试之外)。
这种类型的测试通常涉及构建完整的应用程序二进制文件,
然后在一组有代表性的输入上运行二进制文件(或者在生产负载下,如果它是服务器)以确保所有组件包一起正常工作,而不是单独测试单独的包。
因为集成测试二进制文件是用“go build”而不是“go test”构建的,
直到现在,Go 的工具还没有提供任何简单的方法来收集这些测试的覆盖率概况。
使用 Go 1.20,您现在可以使用“go build -cover”构建覆盖检测程序,然后将这些检测二进制文件提供给集成测试以扩展覆盖测试的范围。
在这篇博文中,我们将举例说明这些新功能的工作原理,并概述一些用于从集成测试中收集覆盖率配置文件的用例和工作流程。
让我们以一个非常小的示例程序为例,为它编写一个简单的集成测试,然后从集成测试中收集覆盖率概况。
对于本练习,我们将使用来自 gitlab.com/golang-commonmark/mdtool 的“mdtool”降价处理工具。
这是一个演示程序,旨在展示客户如何使用包 gitlab.com/golang-commonmark/markdown,这是一个 markdown 到 HTML 的转换库。
为 mdtool 设置 首先让我们下载“mdtool”本身的副本(我们选择一个特定版本只是为了使这些步骤可重现):
git clone cd mdtool git tag example e210a4502a825ef7205691395804eefce536a02f git checkout example
一个简单的集成测试
$ cat integration_test.sh #!/bin/sh BUILDARGS="$*" # # Terminate the test if any command below does not complete successfully. # set -e # # Download some test inputs (the 'website' repo contains various *.md files). # if [ ! -d testdata ]; then git clone https://go.googlesource.com/website testdata git -C testdata tag example 8bb4a56901ae3b427039d490207a99b48245de2c git -C testdata checkout example fi # # Build mdtool binary for testing purposes. # rm -f mdtool.exe go build $BUILDARGS -o mdtool.exe . # # Run the tool on a set of input files from 'testdata'. # FILES=$(find testdata -name "*.md" -print) N=$(echo $FILES | wc -w) for F in $FILES do ./mdtool.exe +x +a $F > /dev/null done echo "finished processing $N files, no crashes" $
这是我们测试的示例运行:
$ /bin/sh integration_test.sh ... finished processing 380 files, no crashes $
成功:我们已经验证了“mdtool”二进制文件成功消化了一组输入文件……但是我们实际使用了多少该工具的源代码?
在下一节中,我们将收集一个覆盖率概况来找出答案。
使用集成测试收集覆盖率数据
$ cat wrap_test_for_coverage.sh #!/bin/sh set -e PKGARGS="$*" # # Setup # rm -rf covdatafiles mkdir covdatafiles # # Pass in "-cover" to the script to build for coverage, then # run with GOCOVERDIR set. # GOCOVERDIR=covdatafiles \ /bin/sh integration_test.sh -cover $PKGARGS # # Post-process the resulting profiles. # go tool covdata percent -i=covdatafiles $
关于上面的包装器需要注意的一些关键事项:
它在运行 integration_test.sh 时传入“-cover”标志,这为我们提供了覆盖检测的“mdtool.exe”二进制文件
它将 GOCOVERDIR 环境变量设置为将写入覆盖率数据文件的目录
当测试完成时,
它运行“go tool covdata percent”来生成一份关于所覆盖语句百分比的报告
这是我们运行这个新包装器脚本时的输出:
$ /bin/sh wrap_test_for_coverage.sh ... gitlab.com/golang-commonmark/mdtool coverage: 48.1% of statements $ # Note: covdatafiles now contains 381 files.
瞧!我们现在对我们的集成测试在运行“mdtool”应用程序的源代码方面的效果有了一些了解。
如果我们进行更改以增强测试工具,然后进行第二次覆盖率收集运行,我们将看到更改反映在覆盖率报告中。例如,
假设我们通过将以下两行添加到 integration_test.sh 来改进我们的测试:
./mdtool.exe +ty testdata/README.md > /dev/null ./mdtool.exe +ta < testdata/README.md > /dev/null
再次运行覆盖测试包装器:
$ /bin/sh wrap_test_for_coverage.sh finished processing 380 files, no crashes gitlab.com/golang-commonmark/mdtool coverage: 54.6% of statements $
我们可以看到更改的效果:语句覆盖率从 48% 增加到 54%。
选择要涵盖的包
默认情况下,“go build -cover”将仅检测属于正在构建的 Go 模块的包,在本例中为 gitlab.com/golang-commonmark/mdtool 包。
然而,在某些情况下,将覆盖检测扩展到其他包是有用的;这可以通过将“-coverpkg”传递给“go build -cover”来完成。
对于我们的示例程序,“mdtool”实际上在很大程度上只是 gitlab.com/golang-commonmark/markdown 包的包装器,
因此,将 markdown 包含在已检测的软件包集中很有趣。
这是“mdtool”的 go.mod 文件:
$ head go.mod module gitlab.com/golang-commonmark/mdtool go 1.17 require ( github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 gitlab.com/golang-commonmark/markdown v0.0.0-20211110145824-bf3e522c626a )
我们可以使用“-coverpkg”标志来控制选择哪些包以包含在覆盖率分析中以包括上面的 deps 之一。这是一个例子:
$ /bin/sh wrap_test_for_coverage.sh -coverpkg=gitlab.com/golang-commonmark/markdown,gitlab.com/golang-commonmark/mdtool ... gitlab.com/golang-commonmark/markdown coverage: 70.6% of statements gitlab.com/golang-commonmark/mdtool coverage: 54.6% of statements $
使用覆盖率数据文件
当覆盖集成测试完成并写出一组原始数据文件(在我们的例子中是 covdatafiles 目录的内容)时,我们可以通过各种方式对这些文件进行后处理。
将配置文件转换为“-coverprofile”文本格式
使用单元测试时,您可以运行 go test -coverprofile=abc。
txt 为给定的覆盖率测试运行编写文本格式的覆盖率配置文件。
对于使用 go build -cover 构建的二进制文件,您可以通过对发送到 GOCOVERDIR 目录中的文件运行 go tool covdata textfmt 来生成文本格式的配置文件。
完成此步骤后,您可以使用 go tool cover -func=
html=
例子:
$ /bin/sh wrap_test_for_coverage.sh ... $ go tool covdata textfmt -i=covdatafiles -o=cov.txt $ go tool cover -func=cov.txt gitlab.com/golang-commonmark/mdtool/main.go:40: readFromStdin 100.0% gitlab.com/golang-commonmark/mdtool/main.go:44: readFromFile 80.0% gitlab.com/golang-commonmark/mdtool/main.go:54: readFromWeb 0.0% gitlab.com/golang-commonmark/mdtool/main.go:64: readInput 80.0% gitlab.com/golang-commonmark/mdtool/main.go:74: extractText 100.0% gitlab.com/golang-commonmark/mdtool/main.go:88: writePreamble 100.0% gitlab.com/golang-commonmark/mdtool/main.go:111: writePostamble 100.0% gitlab.com/golang-commonmark/mdtool/main.go:118: handler 0.0% gitlab.com/golang-commonmark/mdtool/main.go:139: main 51.6% total: (statements) 54.6% $
使用“go tool covdata merge”合并原始配置文件
每次执行“-cover”构建的应用程序都会将一个或多个数据文件写入 GOCOVERDIR 环境变量中指定的目录。如果集成测试执行 N 次程序执行,您将在输出目录中得到 O(N) 个文件。
数据文件中通常有很多重复的内容,因此要压缩数据和/或合并来自不同集成测试运行的数据集,您可以使用 go tool covdata merge 命令将配置文件合并在一起。
例子:
$ /bin/sh wrap_test_for_coverage.sh finished processing 380 files, no crashes gitlab.com/golang-commonmark/mdtool coverage: 54.6% of statements $ ls covdatafiles covcounters.13326b42c2a107249da22f6e0d35b638.772307.1677775306041466651 covcounters.13326b42c2a107249da22f6e0d35b638.772314.1677775306053066987 ... covcounters.13326b42c2a107249da22f6e0d35b638.774973.1677775310032569308 covmeta.13326b42c2a107249da22f6e0d35b638 $ ls covdatafiles | wc 381 381 27401 $ rm -rf merged ; mkdir merged ; go tool covdata merge -i=covdatafiles -o=merged $ ls merged covcounters.13326b42c2a107249da22f6e0d35b638.0.1677775331350024014 covmeta.13326b42c2a107249da22f6e0d35b638 $
go tool covdata merge 操作还接受 -pkg 标志,如果需要,该标志可用于选择特定的包或一组包。
这种合并功能对于组合来自不同类型的测试运行的结果也很有用,包括由其他测试工具生成的运行。
包起来
这涵盖了它:随着 1.20 版本的发布,
Go 的覆盖工具不再局限于包测试,而是支持从更大的集成测试中收集配置文件。
我们希望您能充分利用这些新功能,以帮助了解更大、更复杂的测试的运行情况,以及它们正在运行源代码的哪些部分。
还没有评论,来说两句吧...