🦴

正しいモックの使い方

に公開

テストコードにおけるモックの使い方について、不十分なケースをよく見かけるので記録を残します

🟦 モックを使う際に必ずやるべきこと

  • モック化した関数呼び出しの引数を検証する
  • モック化した関数の呼び出し回数を検証する

🟠 なぜ必要なのか

依存先モジュールの呼び出し方に誤りがあるとバグに繋がります。
この検証をサボってバグを見逃しているケースが多いです(所謂、偽陰性)

🟦 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