Go言語入門:統合テストのカバレッジ -Vol.10-

スポンサーリンク
Go言語入門:統合テストのカバレッジ -Vol.10- ノウハウ
Go言語入門:統合テストのカバレッジ -Vol.10-
この記事は約14分で読めます。
よっしー
よっしー

こんにちは。よっしーです(^^)

本日は、Go言語の.統合テストのカバレッジついて解説しています。

スポンサーリンク

背景

Go言語でテストを書いていると、「go test -coverprofileでカバレッジが取れるのは知っているけど、統合テストのカバレッジってどうやって測るんだろう?」と疑問に思ったことはありませんか?

公式ドキュメントには、Go 1.20から統合テストのカバレッジ測定がサポートされたことが書かれていますが、英語で書かれている上に、ユニットテストとの違いや具体的な手順の説明が簡潔すぎて、初めて読むと「結局どうすればいいの?」と戸惑ってしまうかもしれません。

この記事では、公式ドキュメントの内容を丁寧な日本語に翻訳し、さらに初心者の方でも理解できるように、ユニットテストと統合テストの違い、なぜ3ステップ必要なのか、そして実際にどのようなコマンドを実行すればよいのかを、具体例を交えて解説していきます。

統合テストのカバレッジ測定は一見難しそうに見えますが、仕組みを理解すれば決して複雑ではありません。実際のアプリケーションでどれだけコードがテストされているかを把握することで、より品質の高いソフトウェア開発ができるようになります。一緒に学んでいきましょう!

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
$


解説

外部依存パッケージのカバレッジ測定

問題: デフォルトでは外部パッケージが測定されない

通常のgo build -coverでは:

測定される:
✅ 自分のプロジェクト(メインモジュール)

測定されない:
❌ 標準ライブラリ(fmt, io, netなど)
❌ 外部ライブラリ(go.modに記載されている依存関係)

レストランの例え:

  • 自分の店(メインモジュール)の売上は記録される
  • 仕入れ先(外部依存)の売上は記録されない

解決策: すべての依存関係を測定対象にする

外部ライブラリも含めて測定したい場合の手順を見ていきましょう。

ステップバイステップ解説

ステップ1: 依存パッケージのリストを生成

go list -f '{{if not .Standard}}{{.ImportPath}}{{end}}' -deps . | paste -sd "," > pkgs.txt

このコマンドを分解して理解する:

パート1: go list
go list -deps .

意味:

  • プロジェクトが依存しているすべてのパッケージをリスト表示
  • . = カレントディレクトリ(プロジェクトルート)
  • -deps = 依存関係も含める

出力例(一部):

fmt
io
net/http
mydomain.com
mydomain.com/greetings
rsc.io/quote
rsc.io/sampler
golang.org/x/text/language
パート2: -f (フォーマット指定)
go list -f '{{if not .Standard}}{{.ImportPath}}{{end}}' -deps .

意味:

  • -f = 出力フォーマットを指定
  • {{if not .Standard}} = 標準ライブラリでない場合のみ
  • {{.ImportPath}} = パッケージのインポートパスを出力
  • {{end}} = if文の終わり

.Standardとは:

  • true = 標準ライブラリ(fmt, io など)
  • false = 外部パッケージ

フィルタ後の出力:

mydomain.com
mydomain.com/greetings
rsc.io/quote
rsc.io/sampler
golang.org/x/text/language
golang.org/x/text/internal/tag

標準ライブラリ(fmt, ioなど)が除外されています。

パート3: paste -sd ","
... | paste -sd ","

意味:

  • paste = 行を結合するコマンド
  • -s = すべての行を1行にまとめる
  • -d "," = 区切り文字としてカンマを使用

結果:

mydomain.com,mydomain.com/greetings,rsc.io/quote,rsc.io/sampler,golang.org/x/text/language,golang.org/x/text/internal/tag
パート4: > pkgs.txt
... > pkgs.txt

意味:

  • 結果をファイルpkgs.txtに保存

pkgs.txtの内容:

mydomain.com,mydomain.com/greetings,rsc.io/quote,rsc.io/sampler,golang.org/x/text/language,golang.org/x/text/internal/tag

ステップ2: パッケージリストを使ってビルド

go build -o myprogram.exe -coverpkg=`cat pkgs.txt` .

このコマンドの意味:

-coverpkg=`cat pkgs.txt`
          ↑
          pkgs.txtの内容を読み込んで-coverpkgに渡す

実際に実行されるコマンド(展開後):

go build -o myprogram.exe \
  -coverpkg=mydomain.com,mydomain.com/greetings,rsc.io/quote,rsc.io/sampler,golang.org/x/text/language,golang.org/x/text/internal/tag \
  .

これで、メインモジュールだけでなく、すべての外部依存パッケージもカバレッジ測定の対象になります。

ステップ3: 通常通り実行とレポート生成

# 実行
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

外部パッケージ(rsc.io/quotegolang.org/x/text/languageなど)のカバレッジも表示されています!

完全なスクリプト例

#!/bin/bash
# build-with-full-coverage.sh

echo "=== すべての依存パッケージを含むカバレッジビルド ==="

# ステップ1: 非標準ライブラリパッケージのリストを生成
echo "依存パッケージリストを生成中..."
go list -f '{{if not .Standard}}{{.ImportPath}}{{end}}' -deps . | paste -sd "," > pkgs.txt

echo "対象パッケージ:"
cat pkgs.txt | tr ',' '\n'

# ステップ2: カバレッジ対応でビルド
echo ""
echo "ビルド中..."
go build -cover -o myapp -coverpkg=`cat pkgs.txt` .

# ステップ3: 実行
echo ""
echo "テスト実行中..."
rm -rf coverage
mkdir coverage
GOCOVERDIR=coverage ./myapp

# ステップ4: レポート生成
echo ""
echo "=== カバレッジレポート ==="
go tool covdata percent -i=coverage

# クリーンアップ
rm pkgs.txt

echo ""
echo "完了!"

Windows環境での対応

Windowsではpasteコマンドが使えないので、PowerShellで対応します:

# PowerShell版

# ステップ1: パッケージリストを生成
go list -f '{{if not .Standard}}{{.ImportPath}}{{end}}' -deps . | Out-File -Encoding ASCII temp.txt
$packages = (Get-Content temp.txt) -join ','
$packages | Out-File -Encoding ASCII pkgs.txt

# ステップ2: ビルド
$pkgList = Get-Content pkgs.txt
go build -cover -o myapp.exe -coverpkg=$pkgList .

# ステップ3: 実行
New-Item -ItemType Directory -Force -Path coverage
$env:GOCOVERDIR="coverage"
.\myapp.exe

# ステップ4: レポート
go tool covdata percent -i=coverage

# クリーンアップ
Remove-Item temp.txt, pkgs.txt

より簡単な方法(ワンライナー)

# Linux/Mac
go build -cover -o myapp \
  -coverpkg=$(go list -f '{{if not .Standard}}{{.ImportPath}}{{end}}' -deps . | paste -sd ",") \
  .

# または、標準ライブラリも含めてすべて測定(非推奨)
go build -cover -o myapp \
  -coverpkg=$(go list -deps . | paste -sd ",") \
  .

なぜ標準ライブラリは除外するのか

標準ライブラリを含める場合

# 標準ライブラリも含める(.Standardチェックなし)
go list -f '{{.ImportPath}}' -deps . | paste -sd "," > pkgs.txt
go build -cover -coverpkg=`cat pkgs.txt` -o myapp .

問題点:

  1. ビルド時間が非常に長くなる
    • 標準ライブラリは大量のコードがある
    • すべてをインストルメンテーションすると時間がかかる
  2. バイナリサイズが巨大になる
    • カバレッジ測定コードが大量に埋め込まれる
  3. 実行速度が遅くなる
    • すべてのコードがカバレッジ記録を行う
  4. 実用的な価値が低い
    • 標準ライブラリのカバレッジを知っても意味がない
    • 自分で変更できるコードではない

推奨: 標準ライブラリは除外し、自分のコードと直接の依存パッケージだけを測定対象にする。

実践例: Webアプリケーション

#!/bin/bash
# webapp-full-coverage.sh

PROJECT="mywebapp"
APP="./webapp"

echo "=== $PROJECT のフルカバレッジテスト ==="

# クリーンアップ
rm -rf coverage

# 依存パッケージリスト生成
echo "依存パッケージを収集中..."
go list -f '{{if not .Standard}}{{.ImportPath}}{{end}}' -deps . | paste -sd "," > deps.txt

echo "測定対象パッケージ数:"
cat deps.txt | tr ',' '\n' | wc -l

# ビルド
echo ""
echo "カバレッジ対応でビルド中(時間がかかる場合があります)..."
go build -cover -o $APP -coverpkg=`cat deps.txt` ./cmd/server

# サーバー起動
echo ""
echo "Webサーバー起動中..."
mkdir coverage
GOCOVERDIR=coverage $APP &
SERVER_PID=$!
sleep 3

# テストリクエスト
echo "APIテスト実行中..."
curl -s http://localhost:8080/api/users > /dev/null
curl -s http://localhost:8080/api/posts > /dev/null
curl -s http://localhost:8080/health > /dev/null

# サーバー停止
echo ""
echo "サーバー停止中..."
kill $SERVER_PID
wait $SERVER_PID 2>/dev/null

# レポート生成
echo ""
echo "=== カバレッジレポート ==="
echo ""
echo "【自分のコード】"
go tool covdata percent -i=coverage -pkg=$PROJECT/...

echo ""
echo "【外部依存】"
go tool covdata percent -i=coverage | grep -v "^    $PROJECT"

echo ""
echo "【全体】"
go tool covdata percent -i=coverage

# HTMLレポート
echo ""
echo "詳細HTMLレポート生成中..."
go tool covdata textfmt -i=coverage -o full-coverage.txt
go tool cover -html=full-coverage.txt -o full-coverage.html

echo ""
echo "完了!"
echo "  HTMLレポート: full-coverage.html"

# クリーンアップ
rm deps.txt

特定の外部パッケージだけ測定

# 自分のコード + 特定の外部ライブラリだけ
MY_PACKAGES="mydomain.com/..."
EXTERNAL_LIBS="github.com/gin-gonic/gin,github.com/lib/pq"

go build -cover -o myapp \
  -coverpkg="$MY_PACKAGES,$EXTERNAL_LIBS" \
  .

トラブルシューティング

問題1: ビルドが非常に遅い

# 依存パッケージが多すぎる可能性
cat pkgs.txt | tr ',' '\n' | wc -l
# 100以上だと時間がかかる

解決策:

# 自分のコードと重要な依存だけに絞る
go build -cover -o myapp \
  -coverpkg=mydomain.com/...,重要な外部パッケージ \
  .

問題2: バイナリサイズが巨大

$ ls -lh myapp
-rwxr-xr-x  1 user  staff   150M  myapp  # 通常の10倍以上!

原因: 多くのパッケージをインストルメンテーションしている

対策:

# 開発/テスト用とリリース用を分ける

# テスト用(カバレッジあり、大きい)
go build -cover -coverpkg=... -o myapp-test .

# リリース用(カバレッジなし、小さい)
go build -o myapp .

問題3: 実行が遅い

原因: カバレッジ測定のオーバーヘッド

対策:

  • 本番環境ではカバレッジビルドを使わない
  • 必要最小限のパッケージだけ測定

まとめ

項目説明
デフォルト動作メインモジュールのみ測定
外部依存も測定-coverpkgで明示的に指定
パッケージリスト生成go listで自動生成可能
標準ライブラリ通常は除外(測定しても意味がない)
注意点ビルド時間・サイズ・実行速度に影響

重要なポイント:

  • go listで依存パッケージリストを自動生成
  • 標準ライブラリは通常除外({{if not .Standard}}
  • すべての依存を測定するとオーバーヘッドが大きい
  • 実用的には自分のコード+重要な依存だけ測定するのが良い
  • 本番環境ではカバレッジビルドを使用しない

おわりに 

本日は、Go言語の統合テストのカバレッジについて解説しました。

よっしー
よっしー

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

それでは、また明日お会いしましょう(^^)

コメント

タイトルとURLをコピーしました