Go言語のテストでよくある失敗パターンとその対処法!初心者でもわかるデバッグのコツ
生徒
「Go言語でテストを書いたんですが、なぜかテストが失敗してしまいます。どうしてでしょうか?」
先生
「テストが失敗する原因はいくつかあります。今回は初心者でも理解できるように、よくあるパターンと対処法を紹介します。」
生徒
「お願いします!失敗の原因がわかれば安心です。」
1. 関数の返り値や期待値が間違っている
テストで一番多いのが、関数が返す値と期待する値が合っていないパターンです。Go言語ではt.Errorfやt.Fatalfを使って比較します。
func TestAdd(t *testing.T) {
got := Add(2, 3)
want := 5
if got != want {
t.Errorf("got %d, want %d", got, want)
}
}
ここで、期待値wantが間違っていると、テストは失敗します。まずは期待値が正しいか確認しましょう。
2. テストデータの初期化ミス
テストではデータを初期化してから実行する必要があります。初期化が不十分だと、前のテストの結果が残り失敗することがあります。
func TestWithData(t *testing.T) {
data := []int{1, 2, 3}
// データを初期化してテスト
result := Sum(data)
if result != 6 {
t.Errorf("expected 6, got %d", result)
}
}
毎回データを新しく作り直すことで、テストの独立性を保つことができます。
3. 並行処理やゴルーチンの競合
Go言語の特徴である並行処理(ゴルーチン)を使ったテストでは、データの競合やタイミングによってテストが失敗することがあります。
func TestConcurrent(t *testing.T) {
counter := 0
var mu sync.Mutex
for i := 0; i < 10; i++ {
go func() {
mu.Lock()
counter++
mu.Unlock()
}()
}
time.Sleep(time.Second)
if counter != 10 {
t.Errorf("expected 10, got %d", counter)
}
}
対処法は、sync.Mutexやチャンネルで競合を防ぎ、必ずテストが安定するように制御することです。
4. 外部サービスやデータベース依存
テストで外部のサービスやデータベースを直接使うと、接続の失敗や状態によってテストが不安定になります。これを避けるためにモックを使います。
// モックを使った例
type MockDB struct {}
func (m MockDB) GetData() string { return "mocked" }
モックを使うことで外部依存を切り離し、テストを安定させることができます。
5. タイムアウトの不足
無限ループや遅い処理をテストすると、テストがいつまでも終わらないことがあります。t.Timeoutや-timeoutオプションで制限時間を設定しましょう。
go test -timeout 5s
これにより、テストが一定時間以上かかる場合は自動的に終了し、安全にテストを実行できます。
6. ログやエラーメッセージが不十分
テストが失敗したときに原因がわからないと、修正が難しくなります。t.Logfやt.Errorfを使って詳細な情報を出力しましょう。
t.Logf("current value: %d", value)
テスト結果に必要な情報を出力することで、原因の特定が簡単になります。
7. 初心者でも覚えておきたい失敗回避のポイント
- 関数の期待値と返り値を確認する
- テストデータは毎回初期化する
- 並行処理は競合を防ぐ
- 外部依存はモックで切り離す
- タイムアウトを設定する
- ログやエラーを詳細に出力する
これらの対処法を実践することで、Go言語のテストが安定し、初心者でも失敗の原因を簡単に見つけられます。