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

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

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

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

スポンサーリンク

背景

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

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

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

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

GO111MODULE=offモードでgo build -coverを使用できますか?

はい、go build -coverGO111MODULE=offでも動作します。GO111MODULE=offモードでプログラムをビルドする場合、コマンドライン上でターゲットとして特に指定されたパッケージのみがプロファイリング用にインストルメンテーションされます。プロファイルに追加のパッケージを含めるには、-coverpkgフラグを使用してください。


解説

GO111MODULEとは何か

Goのモジュールシステムの歴史

GO111MODULEは、Goのモジュールシステムの動作を制御する環境変数です。

モード説明使用時期
GO111MODULE=onモジュールモード(推奨)Go 1.16以降のデフォルト
GO111MODULE=offレガシーGOPATHモード古いプロジェクト
GO111MODULE=auto自動判定Go 1.11-1.15のデフォルト

学校の例え:

  • モジュールモード(on) = 新しい教育システム(推奨)
  • GOPATHモード(off) = 古い教育システム(まだ動くが非推奨)

モジュールモードとGOPATHモードの違い

モジュールモード(GO111MODULE=on)- 推奨

# 現代的なプロジェクト構造
myproject/
├── go.mod              # 依存関係の定義
├── go.sum              # 依存関係のチェックサム
├── main.go
└── handlers/
    └── api.go

# ビルド
go build -cover -o myapp .

特徴:

  • go.modファイルで依存関係を管理
  • ✅ どこにでもプロジェクトを配置できる
  • ✅ バージョン管理が明確
  • ✅ これが標準的な方法

GOPATHモード(GO111MODULE=off)- レガシー

# 古いプロジェクト構造(固定の場所が必要)
$GOPATH/
└── src/
    └── github.com/
        └── username/
            └── myproject/
                ├── main.go
                └── handlers/
                    └── api.go

# ビルド
GO111MODULE=off go build -cover -o myapp .

特徴:

  • ⚠️ $GOPATH/src配下に配置する必要がある
  • ⚠️ go.modファイルがない
  • ⚠️ 依存関係の管理が難しい
  • ⚠️ 古い方法(新規プロジェクトでは非推奨)

GO111MODULE=offでのカバレッジビルド

基本的な使い方

# GO111MODULE=offモードでカバレッジビルド
GO111MODULE=off go build -cover -o myapp .

重要な違い: モジュールモードとGOPATHモードでは、デフォルトでインストルメンテーションされるパッケージが異なります。

モジュールモードの場合

# GO111MODULE=on(または未設定で1.16以降)
go build -cover -o myapp .

インストルメンテーションされるパッケージ:

  • ✅ メインモジュール内のすべてのパッケージ(go.modで定義されたモジュール)
測定対象:
✅ mydomain.com/myapp
✅ mydomain.com/myapp/handlers
✅ mydomain.com/myapp/models
✅ mydomain.com/myapp/utils

GOPATHモードの場合

# GO111MODULE=off
GO111MODULE=off go build -cover -o myapp .

インストルメンテーションされるパッケージ:

  • コマンドラインで直接指定したパッケージのみ
測定対象:
✅ 現在のディレクトリのパッケージのみ

測定対象外:
❌ サブパッケージ(handlers, models, utilsなど)

-coverpkgで追加パッケージを指定

GOPATHモードでは、サブパッケージも測定するために-coverpkgが必要です。

例1: サブパッケージを含める

# プロジェクト構造
$GOPATH/src/github.com/user/myapp/
├── main.go              (mainパッケージ)
├── handlers/
│   └── api.go          (handlers パッケージ)
└── models/
    └── user.go         (models パッケージ)

# ❌ デフォルト(mainパッケージのみ)
GO111MODULE=off go build -cover -o myapp .

# ✅ サブパッケージも含める
GO111MODULE=off go build -cover \
  -coverpkg=github.com/user/myapp,github.com/user/myapp/handlers,github.com/user/myapp/models \
  -o myapp .

# または、ワイルドカード使用
GO111MODULE=off go build -cover \
  -coverpkg=github.com/user/myapp/... \
  -o myapp .

実践例1: GOPATHモードでのカバレッジ測定

#!/bin/bash
# gopath-coverage.sh

# GOPATHの確認
echo "GOPATH: $GOPATH"

# プロジェクトのパッケージパス
PKG="github.com/user/myapp"

# すべてのサブパッケージを含める
echo "ビルド中..."
GO111MODULE=off go build -cover \
  -coverpkg=$PKG/... \
  -o myapp \
  $GOPATH/src/$PKG

# 実行
echo "テスト実行中..."
mkdir -p coverage
GOCOVERDIR=coverage ./myapp

# レポート
echo ""
echo "=== カバレッジレポート ==="
go tool covdata percent -i=coverage

モジュールモードとの比較

シナリオ: 同じプロジェクト構造

プロジェクト:
├── main.go
├── handlers/
│   └── api.go
└── models/
    └── user.go

モジュールモード(推奨)

# go.modがあるプロジェクト
cat go.mod
# module github.com/user/myapp
# go 1.20

# シンプルなビルド
go build -cover -o myapp .

# すべてのサブパッケージが自動的に含まれる
GOCOVERDIR=coverage ./myapp
go tool covdata percent -i=coverage
# 出力:
# github.com/user/myapp           coverage: 100.0%
# github.com/user/myapp/handlers  coverage: 85.0%
# github.com/user/myapp/models    coverage: 90.0%

GOPATHモード(レガシー)

# $GOPATH/src/github.com/user/myapp にプロジェクトを配置

# サブパッケージを明示的に指定する必要がある
GO111MODULE=off go build -cover \
  -coverpkg=github.com/user/myapp/... \
  -o myapp .

GOCOVERDIR=coverage ./myapp
go tool covdata percent -i=coverage
# 同じ出力が得られる

違い:

  • モジュールモード: -coverpkg不要(自動)
  • GOPATHモード: -coverpkg必要(手動)

実践例2: レガシープロジェクトのマイグレーション

古いGOPATHプロジェクトを段階的にモジュールモードに移行する例:

#!/bin/bash
# migration-coverage.sh

PKG_PATH="github.com/company/oldproject"
GOPATH_SRC="$GOPATH/src/$PKG_PATH"

echo "=== 古いGOPATHモードでのカバレッジ ==="

# GOPATHモードでビルド
cd $GOPATH_SRC
GO111MODULE=off go build -cover \
  -coverpkg=$PKG_PATH/... \
  -o oldapp .

# 実行
mkdir -p coverage_gopath
GOCOVERDIR=coverage_gopath ./oldapp

# レポート
echo "GOPATHモード結果:"
go tool covdata percent -i=coverage_gopath

echo ""
echo "=== モジュールモードへ移行 ==="

# go.modを作成
go mod init $PKG_PATH
go mod tidy

# モジュールモードでビルド
go build -cover -o newapp .

# 実行
mkdir -p coverage_module
GOCOVERDIR=coverage_module ./newapp

# レポート
echo "モジュールモード結果:"
go tool covdata percent -i=coverage_module

echo ""
echo "移行完了!go.modを確認してください。"

なぜモジュールモードを使うべきか

モジュールモードの利点

  1. どこにでも配置可能
# モジュールモード: どこでもOK
~/projects/myapp/
/tmp/test/myapp/
/var/www/myapp/

# GOPATHモード: $GOPATH/src配下のみ
$GOPATH/src/github.com/user/myapp/
  1. 依存関係の管理が簡単
# go.modで明確
module myapp

require (
    github.com/gin-gonic/gin v1.9.0
    github.com/lib/pq v1.10.9
)
  1. バージョン管理
# 特定のバージョンを指定可能
go get github.com/gin-gonic/gin@v1.9.0
  1. 再現性のあるビルド
# go.sumでハッシュを記録
# チーム全員が同じバージョンを使用

トラブルシューティング

問題1: GOPATHモードでサブパッケージが測定されない

$ GO111MODULE=off go build -cover -o myapp .
$ GOCOVERDIR=coverage ./myapp
$ go tool covdata percent -i=coverage
    main    coverage: 100.0% of statements
# サブパッケージが表示されない

解決策:

# -coverpkgでサブパッケージを明示的に指定
GO111MODULE=off go build -cover \
  -coverpkg=github.com/user/myapp/... \
  -o myapp .

問題2: GOPATHが設定されていない

$ GO111MODULE=off go build -cover -o myapp .
error: cannot find package "..." in any of:
    /usr/local/go/src/... (from $GOROOT)
    ($GOPATH not set)

解決策:

# GOPATHを設定
export GOPATH=$HOME/go

# またはモジュールモードを使用(推奨)
unset GO111MODULE  # デフォルトのモジュールモードを使用
go build -cover -o myapp .

問題3: 混在した環境

# プロジェクトにgo.modがあるのにGO111MODULE=offを使用
$ ls
go.mod  go.sum  main.go

$ GO111MODULE=off go build -cover -o myapp .
# 予期しない動作

解決策:

# go.modがある場合はモジュールモードを使う
go build -cover -o myapp .

# またはgo.modを削除してGOPATHモードに完全移行
# (非推奨)

推奨事項

新規プロジェクト

# ✅ モジュールモードを使用
go mod init github.com/user/myapp
go build -cover -o myapp .

既存のGOPATHプロジェクト

オプション1: モジュールモードへ移行(推奨)

cd $GOPATH/src/github.com/user/myapp
go mod init github.com/user/myapp
go mod tidy
go build -cover -o myapp .

オプション2: GOPATHモードを継続(レガシー)

# -coverpkgを必ず使用
GO111MODULE=off go build -cover \
  -coverpkg=github.com/user/myapp/... \
  -o myapp .

まとめ

モードGO111MODULEデフォルト測定範囲-coverpkgの必要性
モジュールon(推奨)モジュール全体通常不要
GOPATHoff(レガシー)指定パッケージのみサブパッケージ測定に必須

重要なポイント:

  • GO111MODULE=offでもgo build -coverは動作する
  • ⚠️ GOPATHモードではデフォルトで指定パッケージのみ測定
  • -coverpkgでサブパッケージも含められる
  • 🎯 新規プロジェクトはモジュールモードを使用すること
  • 📚 レガシープロジェクトも段階的にモジュールモードへ移行推奨

ベストプラクティス:

# 新規プロジェクト・推奨方法
go mod init myapp
go build -cover -o myapp .

# レガシーサポートが必要な場合のみ
GO111MODULE=off go build -cover -coverpkg=./... -o myapp .

おわりに 

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

よっしー
よっしー

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

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

コメント

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