🦴
正しいモックの使い方
テストコードにおけるモックの使い方について、不十分なケースをよく見かけるので記録を残します
🟦 モックを使う際に必ずやるべきこと
- モック化した関数呼び出しの引数を検証する
- モック化した関数の呼び出し回数を検証する
🟠 なぜ必要なのか
依存先モジュールの呼び出し方に誤りがあるとバグに繋がります。
この検証をサボってバグを見逃しているケースが多いです(所謂、偽陰性)
🟦 Go言語での実装例(uber-go/mock)
🟠 正しい例
mockRepo.EXPECT().
GetUser(userID). // ✅ 引数を検証
Times(1). // ✅ 呼び出し回数を検証
Return(oldUser, nil)
🟠 悪い例
mockRepo.EXPECT().
GetUser(gomock.Any()). // ❌ どんな引数でもOK
AnyTimes(). // ❌ 何回呼ばれてもOK
Return(&User{ID: userID, Name: "Old Name", Age: 30}, nil)
この悪い例では、間違った引数を渡している、重複呼び出ししている、呼び出し漏れ、などのバグを見逃します。
🟠 余談
モックライブラリによっては、クロージャ(任意のロジック)で引数を検証できるものもあります。
引数が膨大な場合はこちらの方が使い勝手が良いです。
mockRepo.EXPECT().
CreateUser(gomock.Any()).
Times(1).
DoAndReturn(func(user *User) error {
// クロージャ内で任意の検証ロジックを実行
assert.Equal(t, "John", user.Name)
assert.Equal(t, 30, user.Age)
assert.Equal(t, "john@example.com", user.Email)
return nil
})
🟦 AIを活用しよう
引数が構造体や連想配列の場合、引数を検証するコードを書くのは大変でしたが、AIの登場により低コストで書けるようになりました。
テストコード作成時のシステムプロンプトに前述の「やるべきこと」を記載しておくのが良いでしょう。
AIによる実装を検証するガードレールとしても効果があります。
以上。正しくモックを使って、バグのない堅牢なコードを書いていきましょう!
Discussion