インテグレーションテストにおけるカバレッジプロファイリングのサポート

目次

概要
カバレッジプロファイリング用のバイナリのビルド
カバレッジ計測済みバイナリの実行
カバレッジデータファイルの操作
よくある質問
リソース
用語集

Go 1.20より、Goはアプリケーションおよびインテグレーションテスト(Goプログラム向けのより大規模で複雑なテスト)からのカバレッジプロファイルの収集をサポートしています。

概要

Goは、「go test -coverprofile=... <pkg_target>」コマンドを介して、パッケージのユニットテストレベルでカバレッジプロファイルを収集するための使いやすいサポートを提供します。Go 1.20から、ユーザーはより大規模なインテグレーションテスト(特定のアプリケーションバイナリの複数の実行を実行する、より重く複雑なテスト)のカバレッジプロファイルを収集できるようになりました。

ユニットテストの場合、カバレッジプロファイルの収集とレポートの生成には2つのステップが必要です。go test -coverprofile=...の実行に続き、go tool cover {-func,-html}を呼び出してレポートを生成します。

インテグレーションテストの場合、以下に示すように、3つのステップが必要です。1つのビルドステップ、1つの実行ステップ(ビルドステップからのバイナリの複数回の呼び出しを伴う場合があります)、そして最後に1つのレポートステップです。

カバレッジプロファイリング用のバイナリのビルド

カバレッジプロファイルを収集するためのアプリケーションをビルドするには、アプリケーションバイナリターゲットで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のちょうど2つのパッケージがプロファイルに含まれ、他の依存パッケージは除外されます。

カバレッジに含めるパッケージをより詳細に制御したいユーザーは、「-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ディレクトリに書き込まれた2つのファイルに注目してください。これらの(バイナリ)ファイルにはカバレッジ結果が含まれています。これらのデータファイルから人間が読める結果を生成する方法の詳細については、次のレポートに関するセクションを参照してください。

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
$

カバレッジデータ出力ファイルには、メタデータファイル(ソースファイル名や関数名など、実行ごとに不変な項目を含む)と、カウンターデータファイル(実行されたプログラムの部分を記録する)の2種類があります。

上記の例では、最初の実行で2つのファイル(カウンターとメタ)が生成され、2回目の実行ではカウンターデータファイルのみが生成されました。メタデータは実行ごとに変わらないため、一度だけ書き込まれればよいのです。

カバレッジデータファイルの操作

Go 1.20では、GOCOVERDIRディレクトリからカバレッジデータファイルを読み取ったり操作したりできる新しいツール「covdata」が導入されました。

Goのcovdataツールはさまざまなモードで実行されます。covdataツールの一般的な呼び出し形式は次のようになります。

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

ここで「-i」フラグは読み取るディレクトリのリストを提供し、各ディレクトリはカバレッジ計測済みバイナリ(GOCOVERDIR経由)の実行から派生したものです。

カバレッジプロファイルレポートの作成

このセクションでは、「go tool covdata」を使用してカバレッジデータファイルから人間が読めるレポートを生成する方法について説明します。

カバーされたステートメントの割合を報告する

各計測済みパッケージの「カバーされたステートメントの割合」メトリックを報告するには、「go tool covdata percent -i=<directory>」コマンドを使用します。上記の実行セクションの例を使用すると

$ 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によって報告されたものと直接対応しています。

従来のテキスト形式への変換

バイナリカバレッジデータファイルを「go test -coverprofile=<outfile>」で生成される従来のテキスト形式に、covdataのtextfmtセレクタを使用して変換できます。結果のテキストファイルは、「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にリストされているインポート)は計測しません。すべての非標準ライブラリの依存関係に対して計測を要求する1つの方法は、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 -coverGO111MODULE=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 1.2におけるユニットテストカバレッジ導入のブログ記事:
    • ユニットテストのカバレッジプロファイリングはGo 1.2リリースの一部として導入されました。詳細についてはこのブログ記事を参照してください。
  • ドキュメント:
    • cmd/goパッケージのドキュメントには、カバレッジに関連するビルドおよびテストフラグが記載されています。
  • 技術詳細:

用語集

ユニットテスト: 特定のGoパッケージに関連付けられた*_test.goファイル内で、Goのtestingパッケージを利用して行われるテスト。

インテグレーションテスト: 特定のアプリケーションまたはバイナリに対する、より包括的で重厚なテスト。インテグレーションテストは通常、プログラムまたは一連のプログラムをビルドし、Goのtestingパッケージに基づく場合とそうでない場合があるテストハーネスの制御下で、複数の入力とシナリオを使用してプログラムの一連の実行を実行することを伴います。