導入
Go言語で開発を行う際、プロジェクトが大きくなるにつれて「テストの実行時間」が開発効率を低下させるボトルネックになりがちです。Goには標準で強力なテスト並列化機能が備わっていますが、ただ使うだけではCPUの競合やリソース不足を招くこともあります。本記事では、`t.Parallel`の基本と、`go test -parallel`フラグを活用してテスト時間を最適化する手法を解説します。
基礎知識
Goのテストで並列化を行うには、主に2つの要素が関係します。
1. t.Parallel(): テスト関数内で呼び出すことで、そのテストを他の並列テストと同時に実行可能にします。
2. go test -parallel n: テスト実行時に同時に走らせる並列テストの最大数を指定するフラグです。
デフォルトでは、この`n`の値は`GOMAXPROCS`(利用可能なCPUコア数)と同じ値に設定されます。しかし、I/O待ち(データベース接続やAPI通信など)が多いテストの場合、CPUコア数以上のテストを同時に走らせたほうが、待機時間を有効活用でき、全体の実行時間を大幅に短縮できます。
実装/解決策
テストを並列化する際は、まずテスト関数内に`t.Parallel()`を記述します。次に、実行環境の特性に合わせて`go test -parallel`で並列数を調整します。例えば、外部APIへのリクエストがボトルネックとなるテストスイートであれば、CPUコア数よりも大きい数(例: 16や32)を指定することで、テスト完了までの時間を短縮可能です。
サンプルプログラム
以下のコードは、`t.Parallel`を用いたテストの記述例です。
package main
import (
“testing”
“time”
)
// 並列実行可能なテストの例
func TestExampleParallel(t testing.T) {
// このメソッドを呼ぶことで、他のt.Parallel()を呼んでいるテストと並列実行される
t.Parallel()
// 模擬的なI/O待ち(例えばDB接続や外部APIリクエスト)
time.Sleep(500 time.Millisecond)
t.Log(“テスト完了”)
}
// 実行コマンド例:
// go test -v -parallel 16 ./…
// 上記コマンドで、同時に最大16個のテストを並列実行する設定になります。
応用・注意点
現場で活用する際の重要な注意点がいくつかあります。
1. 共有リソースの競合: データベースのテーブルを共有しているテストで`t.Parallel`を使うと、デッドロックやデータ競合が発生します。並列化する場合は、テストごとに個別のDBスキーマを作成するか、モックを活用してください。
2. 並列数の過剰設定: 並列数を大きくしすぎると、メモリ消費量が増大し、逆にOSのコンテキストスイッチが発生してパフォーマンスが低下します。特にメモリ負荷が高いテストでは注意が必要です。
3. テストの独立性: `t.Parallel`を有効にするテストは、外部の状態に依存せず、いかなる順序で実行されても成功するように設計されている必要があります。
まずは、I/O待ちが頻発するユニットテストから`t.Parallel`を導入し、CI環境の実行時間を確認しながら`go test -parallel`の値をチューニングしてみてください。

コメント