
こんにちは。よっしーです(^^)
本日は、Go言語の.統合テストのカバレッジついて解説しています。
背景
Go言語でテストを書いていると、「go test -coverprofileでカバレッジが取れるのは知っているけど、統合テストのカバレッジってどうやって測るんだろう?」と疑問に思ったことはありませんか?
公式ドキュメントには、Go 1.20から統合テストのカバレッジ測定がサポートされたことが書かれていますが、英語で書かれている上に、ユニットテストとの違いや具体的な手順の説明が簡潔すぎて、初めて読むと「結局どうすればいいの?」と戸惑ってしまうかもしれません。
この記事では、公式ドキュメントの内容を丁寧な日本語に翻訳し、さらに初心者の方でも理解できるように、ユニットテストと統合テストの違い、なぜ3ステップ必要なのか、そして実際にどのようなコマンドを実行すればよいのかを、具体例を交えて解説していきます。
統合テストのカバレッジ測定は一見難しそうに見えますが、仕組みを理解すれば決して複雑ではありません。実際のアプリケーションでどれだけコードがテストされているかを把握することで、より品質の高いソフトウェア開発ができるようになります。一緒に学んでいきましょう!
-coverpkg=mainで自分の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
解説
パッケージ名とインポートパスの違い
よくある間違い
// main.go
package main // ← これは「パッケージ名」
import "fmt"
func main() {
fmt.Println("Hello")
}
多くの初心者が以下のように考えます:
# ❌ 間違い: パッケージ名で指定
go build -coverpkg=main -o myapp .
結果:
warning: no packages being built depend on matches for pattern main
# 警告が出て、カバレッジが測定されない
パッケージ名 vs インポートパス
パッケージ名
package main // ← これがパッケージ名
package handlers
package models
特徴:
- ソースコード内で
packageキーワードの後に書くもの - 同じディレクトリ内のすべての
.goファイルで同じ名前を使う - これは
-coverpkgで使えない
インポートパス
mydomain.com ← これがインポートパス
mydomain.com/handlers
mydomain.com/models
github.com/user/project
特徴:
go.modのmodule名から始まる完全パス- 他のパッケージから
importする時に使うパス - これを
-coverpkgで使う
プロジェクトのインポートパスを確認する方法
方法1: go list -m
$ go list -m
mydomain.com
これがプロジェクトのモジュールパス(ベースインポートパス)です。
方法2: go.modファイルを確認
$ cat go.mod
module mydomain.com
go 1.20
最初の行のmoduleの後がモジュールパス=ルートのインポートパスです。
方法3: プロジェクト構造から推測
myproject/
├── go.mod (module github.com/user/myproject)
├── main.go → github.com/user/myproject
├── handlers/
│ └── api.go → github.com/user/myproject/handlers
└── models/
└── user.go → github.com/user/myproject/models
正しい指定方法
例1: シンプルなプロジェクト
# プロジェクト構造
myproject/
├── go.mod (module mydomain.com)
└── main.go (package main)
# インポートパスを確認
$ go list -m
mydomain.com
# ❌ 間違い: パッケージ名で指定
$ go build -coverpkg=main -o myapp .
warning: no packages being built depend on matches for pattern main
# ✅ 正解: インポートパスで指定
$ go build -coverpkg=mydomain.com -o myapp .
例2: GitHubプロジェクト
# プロジェクト構造
myproject/
├── go.mod (module github.com/user/myproject)
└── main.go (package main)
# インポートパスを確認
$ go list -m
github.com/user/myproject
# ❌ 間違い
$ go build -coverpkg=main -o myapp .
# ✅ 正解
$ go build -coverpkg=github.com/user/myproject -o myapp .
例3: サブパッケージを含むプロジェクト
# プロジェクト構造
myproject/
├── go.mod (module example.com/myapp)
├── main.go (package main)
├── handlers/
│ └── api.go (package handlers)
└── models/
└── user.go (package models)
# ルートパッケージのみ
$ go build -coverpkg=example.com/myapp -o myapp .
# すべてのサブパッケージを含む
$ go build -coverpkg=example.com/myapp/... -o myapp .
実際のエラーケース
エラー例1: パッケージ名を使用
$ cat go.mod
module mywebapp
$ cat main.go
package main
import "fmt"
func main() {
fmt.Println("Hello")
}
# ❌ パッケージ名で指定
$ go build -coverpkg=main -o webapp .
warning: no packages being built depend on matches for pattern main
# 実行してもカバレッジが記録されない
$ mkdir coverage
$ GOCOVERDIR=coverage ./webapp
Hello
$ go tool covdata percent -i=coverage
# 何も表示されない
修正版
# ✅ インポートパスで指定
$ go build -coverpkg=mywebapp -o webapp .
$ GOCOVERDIR=coverage ./webapp
Hello
$ go tool covdata percent -i=coverage
mywebapp coverage: 100.0% of statements
なぜこの設計なのか
理由1: パッケージ名は重複する可能性がある
プロジェクト構造:
├── go.mod (module myapp)
├── main.go (package main) ← mainという名前
├── cmd/
│ └── tool/
│ └── main.go (package main) ← これもmain!
└── internal/
└── main/
└── core.go (package main) ← これもmain!!
3つすべてpackage mainですが、インポートパスは異なります:
myappmyapp/cmd/toolmyapp/internal/main
理由2: インポートパスは一意
インポートパスは常にプロジェクト内で一意なので、正確にパッケージを特定できます。
パッケージ名とインポートパスの対応表
| ディレクトリ | package宣言 | インポートパス(-coverpkgで使用) |
|---|---|---|
/ | package main | myapp |
/handlers | package handlers | myapp/handlers |
/models | package models | myapp/models |
/cmd/tool | package main | myapp/cmd/tool |
/internal/utils | package utils | myapp/internal/utils |
実践スクリプト例
#!/bin/bash
# build-with-correct-coverpkg.sh
# モジュールパスを自動取得
MODULE_PATH=$(go list -m)
echo "モジュールパス: $MODULE_PATH"
# すべてのサブパッケージを含める
echo "ビルド中..."
go build -cover -coverpkg=$MODULE_PATH/... -o myapp .
# 実行
echo "テスト実行中..."
rm -rf coverage
mkdir coverage
GOCOVERDIR=coverage ./myapp
# レポート
echo ""
echo "=== カバレッジレポート ==="
go tool covdata percent -i=coverage
echo ""
echo "完了!"
より高度な使い方
パターン1: 複数のモジュール
# 複数のプロジェクトが同じリポジトリにある場合
myrepo/
├── serviceA/
│ ├── go.mod (module github.com/user/serviceA)
│ └── main.go
└── serviceB/
├── go.mod (module github.com/user/serviceB)
└── main.go
# serviceAをビルド
cd serviceA
go build -coverpkg=github.com/user/serviceA/... -o serviceA .
# serviceBをビルド
cd ../serviceB
go build -coverpkg=github.com/user/serviceB/... -o serviceB .
パターン2: ベンダリングを使用
# vendor/ ディレクトリを使っている場合
myproject/
├── go.mod (module myapp)
├── vendor/
│ └── github.com/external/lib/
└── main.go
# 自分のコードのみ測定(vendorは除外)
go build -coverpkg=myapp/... -o myapp .
トラブルシューティング
問題1: 警告が出る
$ go build -coverpkg=main -o myapp .
warning: no packages being built depend on matches for pattern main
解決策:
# インポートパスを確認
go list -m
# 正しいインポートパスを使用
go build -coverpkg=$(go list -m) -o myapp .
問題2: カバレッジが0%
$ go tool covdata percent -i=coverage
# 何も表示されない、または0%
原因: -coverpkgで指定したパスが間違っている
確認方法:
# ビルド時の警告を確認
go build -coverpkg=wrong/path -o myapp .
# warning が出ていないか確認
# または、デフォルト(指定なし)で試す
go build -cover -o myapp .
GOCOVERDIR=coverage ./myapp
go tool covdata percent -i=coverage
問題3: どのインポートパスを使えばいいか分からない
# 利用可能なすべてのパッケージをリスト
go list ./...
# 出力例:
# myapp
# myapp/handlers
# myapp/models
# myapp/utils
# これらがインポートパスなので、-coverpkgで使える
go build -coverpkg=myapp,myapp/handlers,myapp/models -o myapp .
# または全部含める
go build -coverpkg=myapp/... -o myapp .
ヘルパー関数
# .bashrcや.zshrcに追加できるヘルパー関数
# 現在のモジュールパスでカバレッジビルド
gobuild-cov() {
local module_path=$(go list -m)
echo "Building with coverage for: $module_path/..."
go build -cover -coverpkg=$module_path/... -o "${1:-app}" .
}
# 使用例:
# $ gobuild-cov myapp
# Building with coverage for: mydomain.com/...
# $ GOCOVERDIR=coverage ./myapp
まとめ
| 項目 | 説明 | 例 |
|---|---|---|
| パッケージ名 | package宣言で使う名前 | main, handlers |
| インポートパス | モジュールパス+ディレクトリパス | myapp, myapp/handlers |
-coverpkgで使用 | インポートパス(パッケージ名ではない) | -coverpkg=myapp |
| 確認コマンド | go list -m | モジュールパスを表示 |
| 全パッケージ指定 | モジュールパス/... | -coverpkg=myapp/... |
重要なポイント:
- ❌
-coverpkg=mainは動かない(パッケージ名) - ✅
-coverpkg=mydomain.comが正しい(インポートパス) go list -mでモジュールパスを確認go.modのmodule行を見る- インポートパスは一意、パッケージ名は重複する可能性がある
クイックリファレンス:
# インポートパスを確認
go list -m
# そのパスを-coverpkgで使用
go build -cover -coverpkg=$(go list -m)/... -o myapp .
# または手動で
go build -cover -coverpkg=mydomain.com/... -o myapp .
おわりに
本日は、Go言語の統合テストのカバレッジについて解説しました。

何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)

コメント