Go 部落格

Go 整合測試的程式碼覆蓋率

Than McIntosh
2023 年 3 月 8 日

程式碼覆蓋率工具可幫助開發人員確定在執行給定的測試套件時,原始碼庫中有多少比例的程式碼被執行(覆蓋)。

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” Markdown 處理工具。這是一個演示程式,旨在展示客戶端如何使用 gitlab.com/golang-commonmark/markdown 包,這是一個 Markdown 到 HTML 的轉換庫。

mdtool 的設定

首先,讓我們下載 “mdtool” 本身的一個副本(我們選擇一個特定版本只是為了使這些步驟可重現)

$ git clone https://gitlab.com/golang-commonmark/mdtool.git
...
$ cd mdtool
$ git tag example e210a4502a825ef7205691395804eefce536a02f
$ git checkout example
...
$

一個簡單的整合測試

現在,我們將為 “mdtool” 編寫一個簡單的整合測試;我們的測試將構建 “mdtool” 二進位制檔案,然後在一組輸入 Markdown 檔案上執行它。這個非常簡單的指令碼在測試資料目錄中的每個檔案上執行 “mdtool” 二進位制檔案,以確保它產生一些輸出並且不會崩潰。

$ 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” 標誌來控制在覆蓋率分析中選擇包含哪個包,以包含上述一個依賴項。這是一個示例。

$ /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=<file>go tool cover -html=<file> 來解釋/視覺化資料,就像使用 go test -coverprofile 一樣。

示例

$ /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 的覆蓋率工具不再侷限於包測試,而是支援從更大型的整合測試收集配置檔案。我們希望您能充分利用這些新功能,幫助您瞭解更大型、更復雜的測試效果如何,以及它們正在執行您原始碼的哪些部分。

請嘗試這些新功能,一如既往,如果您遇到問題,請在我們的 GitHub 問題跟蹤器 上提交問題。謝謝。

下一篇文章:Go 開發者調查 2023 年第一季度結果
上一篇文章:你所有的可比較型別
部落格索引