【Go言語学習|豆知識】テストの「隠れた依存」を暴く!Goの -shuffle オプション活用術

1. 導入: なぜテストの順番をランダムにする必要があるのか

皆さんのプロジェクトで、「特定のテストを個別に実行すると成功するのに、全テストを一括で実行すると失敗する」という怪奇現象に悩まされたことはありませんか?これは、テスト間で共有変数や外部リソース(DBやグローバル設定など)を汚染し合う「暗黙の依存関係」が原因です。Goの `-shuffle` オプションは、実行順序をランダムにすることで、こうした脆弱なテスト(Flaky tests)を強制的にあぶり出し、堅牢なコードベースを維持するために不可欠なツールです。

2. 基礎知識: テストの依存関係とFlaky tests

通常、Goのテストは定義された順番やパッケージ順で実行されます。しかし、テストコードが適切に分離されていないと、あるテストが書き換えたグローバル変数の影響を別のテストが受けてしまいます。このような「実行順序に依存するテスト」をFlaky tests(不安定なテスト)と呼びます。これらは開発者の生産性を著しく低下させ、本来修正すべきバグの発見を遅らせる原因となります。

3. 実装/解決策: -shuffle の使い方

使い方は非常にシンプルです。テスト実行コマンドに `-shuffle=on` を付加するだけです。

コマンド例:
go test -shuffle=on ./…

このコマンドを実行すると、Goはランダムなシード値を用いてテストの実行順序を入れ替えます。もしテストが失敗した場合、コンソールに `randseed=1715678901` のようなシード値が表示されます。この数値をメモしておけば、`-shuffle=1715678901` と指定することで、失敗したときと全く同じ順序を再現してデバッグを行うことができます。

4. サンプルプログラム: 依存関係によるバグの再現例

以下のコードは、グローバル変数を介して依存し合っているため、実行順序によって結果が変わる「悪い例」です。

package example_test

import (
“testing”
)

// 共有されるグローバル変数
var globalConfig = “default”

func TestA(t testing.T) {
// globalConfigを書き換える
globalConfig = “A”
if globalConfig != “A” {
t.Errorf(“期待値はAですが、実際は%sです”, globalConfig)
}
}

func TestB(t testing.T) {
// TestAの後に実行されると失敗する
if globalConfig != “default” {
t.Errorf(“期待値はdefaultですが、実際は%sです”, globalConfig)
}
}

// 実行方法:
// go test -shuffle=on .
// 上記を実行すると、TestBがTestAの後に実行された場合に失敗が検知できます。

5. 応用・注意点: 現場で役立つ運用ルール

現場で `-shuffle` を最大限活用するためのポイントをまとめました。

CI環境での常時有効化
GitHub ActionsなどのCIパイプラインにおいて、テスト実行時に `-shuffle=on` をデフォルトで含めることを強く推奨します。これにより、誰かが「実行順序に依存するテスト」をコミットした瞬間に検知できます。

テストの独立性を高める工夫
依存関係を解消するために、テストごとのセットアップとクリーンアップには `t.Cleanup()` を活用しましょう。

例:
func TestWithCleanup(t testing.T) {
old := globalConfig
// テスト終了後に必ず元に戻す
t.Cleanup(func() { globalConfig = old })

globalConfig = “changed”
// テスト処理…
}

`-shuffle` は単なるデバッグツールではなく、あなたのテストスイートの品質を証明するための品質保証ツールです。ぜひ今日の開発から取り入れてみてください。

コメント

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