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

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

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

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

スポンサーリンク

背景

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

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

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

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

マージ(統合)

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
$ mkdir merged
$ go tool covdata merge -i=windows_datadir,macos_datadir -o merged
$

上記のマージ操作は、指定された入力ディレクトリからデータを結合し、新しいマージされたデータファイルのセットを”merged”ディレクトリに書き込みます。


解説

merge モード: カバレッジデータの統合

マージとは何か

複数の異なるカバレッジデータセットを1つに統合する機能です。

レストランの例え:

  • ランチタイムの売上記録(昼のデータ)
  • ディナータイムの売上記録(夜のデータ)
  • これらを統合して「1日全体の売上記録」を作成

基本的な使い方

コマンド構文

go tool covdata merge -i=<入力ディレクトリ1,入力ディレクトリ2,...> -o <出力ディレクトリ>

具体例:

# 2つのデータを統合
go tool covdata merge -i=dir1,dir2 -o merged

# 複数のデータを統合
go tool covdata merge -i=dir1,dir2,dir3,dir4 -o combined

なぜマージが必要なのか

ユースケース1: クロスプラットフォームテスト

# Windows環境でテスト実行
# (Windows上で)
go build -cover -o myapp.exe .
mkdir windows_coverage
GOCOVERDIR=windows_coverage ./myapp.exe

# macOS環境でテスト実行
# (macOS上で)
go build -cover -o myapp .
mkdir macos_coverage
GOCOVERDIR=macos_coverage ./myapp

# Linux環境でテスト実行
# (Linux上で)
go build -cover -o myapp .
mkdir linux_coverage
GOCOVERDIR=linux_coverage ./myapp

# すべてのプラットフォームのデータを統合
# (どの環境でも可能)
mkdir cross_platform
go tool covdata merge \
  -i=windows_coverage,macos_coverage,linux_coverage \
  -o cross_platform

# 統合結果を確認
go tool covdata percent -i=cross_platform

メリット:

  • プラットフォーム固有のコードパスを含む全体像が見える
  • 「すべての環境で何%カバーされているか」がわかる

ユースケース2: チーム開発

# Aさんの担当機能のテスト結果
mkdir coverage_teamA

# Bさんの担当機能のテスト結果
mkdir coverage_teamB

# Cさんの担当機能のテスト結果
mkdir coverage_teamC

# チーム全体のカバレッジを統合
mkdir coverage_all_team
go tool covdata merge \
  -i=coverage_teamA,coverage_teamB,coverage_teamC \
  -o coverage_all_team

# プロジェクト全体のカバレッジ
go tool covdata percent -i=coverage_all_team

ユースケース3: 段階的なテスト

# フェーズ1: 基本機能テスト
GOCOVERDIR=coverage/phase1 ./myapp basic-tests

# フェーズ2: 応用機能テスト
GOCOVERDIR=coverage/phase2 ./myapp advanced-tests

# フェーズ3: エラーハンドリングテスト
GOCOVERDIR=coverage/phase3 ./myapp error-tests

# すべてのフェーズを統合
mkdir coverage/all_phases
go tool covdata merge \
  -i=coverage/phase1,coverage/phase2,coverage/phase3 \
  -o coverage/all_phases

# 全体のカバレッジ確認
go tool covdata percent -i=coverage/all_phases

マージの仕組み

入力データ

windows_datadir/
├── covmeta.f3833f80...      # Windowsビルドのメタデータ
├── covcounters.xxx.001.yyy  # 実行1
└── covcounters.xxx.002.yyy  # 実行2

macos_datadir/
├── covmeta.b245ad84...      # macOSビルドのメタデータ
├── covcounters.zzz.001.www  # 実行1
└── covcounters.zzz.002.www  # 実行2

重要な注意点:

  • ハッシュ値が異なる(f3833f80... vs b245ad84...
  • これは同じソースコードでも異なるプラットフォームでビルドしたため

マージ後のデータ

go tool covdata merge -i=windows_datadir,macos_datadir -o merged
merged/
├── covmeta.統合されたメタデータ
└── covcounters.統合されたカウンター

マージ操作により:

  • すべてのメタデータが統合される
  • すべてのカウンターデータが統合される
  • 重複するコードパスは自動的に考慮される

実践例1: クロスプラットフォームテスト

#!/bin/bash
# cross-platform-merge.sh

# プロジェクトのルートディレクトリ
PROJECT_ROOT=$(pwd)

# 各プラットフォームのデータディレクトリ
WINDOWS_DATA="$PROJECT_ROOT/coverage/windows"
MACOS_DATA="$PROJECT_ROOT/coverage/macos"
LINUX_DATA="$PROJECT_ROOT/coverage/linux"
MERGED_DATA="$PROJECT_ROOT/coverage/cross_platform"

# マージ前の確認
echo "=== 各プラットフォームのカバレッジ ==="

if [ -d "$WINDOWS_DATA" ]; then
    echo "Windows:"
    go tool covdata percent -i=$WINDOWS_DATA
fi

if [ -d "$MACOS_DATA" ]; then
    echo "macOS:"
    go tool covdata percent -i=$MACOS_DATA
fi

if [ -d "$LINUX_DATA" ]; then
    echo "Linux:"
    go tool covdata percent -i=$LINUX_DATA
fi

# マージ実行
echo ""
echo "=== カバレッジデータを統合中 ==="
mkdir -p $MERGED_DATA

# 存在するディレクトリだけを収集
MERGE_DIRS=""
[ -d "$WINDOWS_DATA" ] && MERGE_DIRS="$WINDOWS_DATA"
[ -d "$MACOS_DATA" ] && MERGE_DIRS="$MERGE_DIRS,$MACOS_DATA"
[ -d "$LINUX_DATA" ] && MERGE_DIRS="$MERGE_DIRS,$LINUX_DATA"

# 先頭のカンマを削除
MERGE_DIRS=${MERGE_DIRS#,}

go tool covdata merge -i=$MERGE_DIRS -o $MERGED_DATA

# マージ後の結果
echo ""
echo "=== クロスプラットフォーム総合カバレッジ ==="
go tool covdata percent -i=$MERGED_DATA

# HTMLレポート生成
echo ""
echo "HTMLレポートを生成中..."
go tool covdata textfmt -i=$MERGED_DATA -o merged-profile.txt
go tool cover -html=merged-profile.txt -o cross-platform-coverage.html

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

実践例2: CI/CDでの活用

# .github/workflows/coverage-merge.yml
name: Cross-Platform Coverage

on: [push, pull_request]

jobs:
  test-windows:
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-go@v2
        with:
          go-version: 1.20
      
      - name: Run tests with coverage
        run: |
          go build -cover -o myapp.exe .
          mkdir coverage
          $env:GOCOVERDIR="coverage"; .\myapp.exe
      
      - name: Upload coverage data
        uses: actions/upload-artifact@v2
        with:
          name: coverage-windows
          path: coverage/

  test-macos:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-go@v2
        with:
          go-version: 1.20
      
      - name: Run tests with coverage
        run: |
          go build -cover -o myapp .
          mkdir coverage
          GOCOVERDIR=coverage ./myapp
      
      - name: Upload coverage data
        uses: actions/upload-artifact@v2
        with:
          name: coverage-macos
          path: coverage/

  test-linux:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-go@v2
        with:
          go-version: 1.20
      
      - name: Run tests with coverage
        run: |
          go build -cover -o myapp .
          mkdir coverage
          GOCOVERDIR=coverage ./myapp
      
      - name: Upload coverage data
        uses: actions/upload-artifact@v2
        with:
          name: coverage-linux
          path: coverage/

  merge-coverage:
    needs: [test-windows, test-macos, test-linux]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-go@v2
        with:
          go-version: 1.20
      
      - name: Download all coverage data
        uses: actions/download-artifact@v2
      
      - name: Merge coverage data
        run: |
          mkdir merged
          go tool covdata merge \
            -i=coverage-windows,coverage-macos,coverage-linux \
            -o merged
      
      - name: Generate report
        run: |
          go tool covdata textfmt -i=merged -o coverage.txt
          go tool cover -html=coverage.txt -o coverage.html
      
      - name: Display coverage
        run: go tool covdata percent -i=merged
      
      - name: Upload merged report
        uses: actions/upload-artifact@v2
        with:
          name: merged-coverage-report
          path: coverage.html

マージと通常の統合の違い

パターンA: 同じディレクトリに実行(マージ不要)

# 複数回実行、同じディレクトリに蓄積
GOCOVERDIR=coverage ./myapp test1
GOCOVERDIR=coverage ./myapp test2
GOCOVERDIR=coverage ./myapp test3

# 自動的に統合される
go tool covdata percent -i=coverage

仕組み:

  • すべてのカウンターファイルが同じディレクトリに蓄積
  • レポート生成時に自動的に統合される
  • マージコマンド不要

パターンB: 別々のディレクトリ(マージ必要)

# 別々のディレクトリに保存
GOCOVERDIR=coverage/test1 ./myapp test1
GOCOVERDIR=coverage/test2 ./myapp test2
GOCOVERDIR=coverage/test3 ./myapp test3

# マージが必要
mkdir coverage/merged
go tool covdata merge \
  -i=coverage/test1,coverage/test2,coverage/test3 \
  -o coverage/merged

# マージ後のデータでレポート生成
go tool covdata percent -i=coverage/merged

いつマージが必要か:

  • 異なるマシンで実行した結果を統合
  • 異なるプラットフォームで実行した結果を統合
  • 異なる時期に実行した結果を統合
  • データを整理して別ディレクトリに保存した場合

複数ディレクトリの指定と統合の違い

方法1: -iで複数ディレクトリ指定(一時的な統合)

# その場で複数ディレクトリを読み込む
go tool covdata percent -i=dir1,dir2,dir3

# 実行のたびに統合処理が必要
go tool covdata textfmt -i=dir1,dir2,dir3 -o report.txt

方法2: mergeで物理的に統合(永続的な統合)

# 一度マージして新しいディレクトリに保存
go tool covdata merge -i=dir1,dir2,dir3 -o merged

# 以降はmergedディレクトリだけ使える
go tool covdata percent -i=merged
go tool covdata textfmt -i=merged -o report.txt
go tool covdata func -i=merged

mergeのメリット:

  • データが物理的に統合される
  • 以降の処理が高速(毎回統合しなくて済む)
  • データのバックアップや共有が簡単

実践例3: 段階的テストの統合

#!/bin/bash
# progressive-test-merge.sh

APP="./myapp"
BASE_DIR="coverage"

# クリーンアップ
rm -rf $BASE_DIR
mkdir -p $BASE_DIR/{smoke,functional,regression,merged}

# ビルド
echo "ビルド中..."
go build -cover -o $APP .

# フェーズ1: スモークテスト(最小限の動作確認)
echo "=== フェーズ1: スモークテスト ==="
GOCOVERDIR=$BASE_DIR/smoke $APP --test=smoke
go tool covdata percent -i=$BASE_DIR/smoke

# フェーズ2: 機能テスト
echo ""
echo "=== フェーズ2: 機能テスト ==="
GOCOVERDIR=$BASE_DIR/functional $APP --test=functional
go tool covdata percent -i=$BASE_DIR/functional

# フェーズ3: リグレッションテスト
echo ""
echo "=== フェーズ3: リグレッションテスト ==="
GOCOVERDIR=$BASE_DIR/regression $APP --test=regression
go tool covdata percent -i=$BASE_DIR/regression

# すべてを統合
echo ""
echo "=== すべてのテストを統合中 ==="
go tool covdata merge \
  -i=$BASE_DIR/smoke,$BASE_DIR/functional,$BASE_DIR/regression \
  -o $BASE_DIR/merged

# 最終結果
echo ""
echo "=== 総合カバレッジレポート ==="
go tool covdata percent -i=$BASE_DIR/merged

# 詳細レポート生成
echo ""
echo "詳細レポートを生成中..."
go tool covdata textfmt -i=$BASE_DIR/merged -o final-coverage.txt
go tool cover -func=final-coverage.txt

echo ""
echo "完了!"
echo "  スモークテスト: $BASE_DIR/smoke"
echo "  機能テスト: $BASE_DIR/functional"
echo "  リグレッションテスト: $BASE_DIR/regression"
echo "  統合結果: $BASE_DIR/merged"

トラブルシューティング

問題1: マージ後のデータが期待より少ない

$ go tool covdata merge -i=dir1,dir2 -o merged
$ go tool covdata percent -i=merged
# カバレッジが期待より低い

原因:

  • 入力ディレクトリのパスが間違っている
  • 一部のディレクトリが空

確認方法:

# 各ディレクトリの内容を確認
ls -la dir1/
ls -la dir2/

# 各ディレクトリのカバレッジを個別確認
go tool covdata percent -i=dir1
go tool covdata percent -i=dir2

問題2: 互換性のないデータ

$ go tool covdata merge -i=old_data,new_data -o merged
error: incompatible coverage data

原因:

  • 異なるバージョンのGoでビルド
  • ソースコードが異なる

解決策:

# 同じバージョンのGoで再ビルド
go version

# すべての環境で同じソースから再ビルド

問題3: 出力ディレクトリが既に存在

$ go tool covdata merge -i=dir1,dir2 -o existing_dir
error: output directory already exists

解決策:

# 既存ディレクトリを削除
rm -rf existing_dir

# または別の名前を使用
go tool covdata merge -i=dir1,dir2 -o merged_new

まとめ

項目説明
コマンドgo tool covdata merge -i=<入力dirs> -o <出力dir>
用途複数のカバレッジデータを1つに統合
入力カンマ区切りのディレクトリリスト
出力新しい統合されたデータディレクトリ
主な用途クロスプラットフォーム、チーム開発、段階的テスト

重要なポイント:

  • mergeで物理的にデータを統合
  • 異なる環境のデータを1つにまとめられる
  • マージ後は通常のデータとして扱える
  • CI/CDパイプラインで特に有用
  • マージは永続的(一度実行すれば以降は不要)

次の記事では、さらに高度なcovdataの機能(パッケージのフィルタリングなど)を見ていきます。

おわりに 

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

よっしー
よっしー

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

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

コメント

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