テストコードを書いていると、誰もが一度は壁にぶつかります。
「これはMock?それともStub?」と迷う瞬間です。
どれも単なる「偽のオブジェクト」に見えますが、コードレビューで「それはMockではなくStubですよ」と言われると、頭が真っ白になりますよね。
この記事では、テストダブルの5兄弟(Dummy、Stub、Spy、Mock、Fake)の違いを、例とともにわかりやすく整理します。
まず結論からお伝えします。
テストダブルは「何を検証するか」で分かれます。値を返すのがStub、呼び出しを記録するのがSpy、期待した呼び出しか判定するのがMock、本物のように動作するのがFakeです。
この一文だけ覚えておけば、半分は理解できたも同然です。
そもそもテストダブルとは?
Test Doubleは、映画の「スタントダブル(stunt double)」に由来する言葉です。
俳優の代わりに危険なシーンを演じる代役のように、本物のオブジェクトを置き換える偽のオブジェクトをまとめて呼ぶ用語です。
この概念は、Gerard Meszarosの『xUnit Test Patterns』(2007年)で体系的に整理されました。
つまり、MockやStubはテストダブルの下位分類です。
なぜ使うのでしょうか。たとえば決済ロジックをテストするのに、本物のカード会社のAPIを呼び出すわけにはいきません。
遅いですし、お金もかかり、ネットワークが切れればテストも失敗します。
そこで、本物の代わりとなる代役を用意するわけです。
Mock・Stub・Spy・Fakeの違いを1つの表で整理
言葉だけでは混乱しやすいので、まず表を見ていきましょう。
| 種類 | 主な役割 | 検証対象 |
|---|---|---|
| Dummy | 置き場所を埋める | 検証しない |
| Stub | 決められた値を返す | 状態(結果値) |
| Spy | 呼び出し履歴を記録 | 状態+呼び出し履歴 |
| Mock | 期待した呼び出しか判定 | 振る舞い(呼び出し) |
| Fake | 軽量に本物のように動作 | 状態(結果値) |
ここで最も重要な境界線は、たった1つです。
状態検証か、振る舞い検証か。
StubとFakeは「結果がこのようになったか」を確認します(状態検証)。
Mockは「そのメソッドが実際に呼び出されたか」を確認します(振る舞い検証)。
Spyはその中間で、呼び出しをこっそり記録する役割です。
コードで見ると一気にわかります
言葉よりコードのほうが早いですよね。ユーザー通知機能を例にします。Swiftでは、プロトコルに準拠したテストダブルを自分で作って使うのが一般的です。
まずはStubです。決められた値だけを返す代役です。
// getName常に「ホン・ギルドン」を返すよう固定
final class UserRepositoryStub: UserRepository {
func getName(id: Int) -> String { "ホン・ギルドン" }
}
let service = GreetingService(repository: UserRepositoryStub())
// テストでは結果値(状態)が正しいかだけを確認
#expect(service.greet(id: 1) == "ホン・ギルドン")
次はMockです。「このメソッドが呼び出されたか」を検証します。
// 通知が実際に送信された(呼び出された)かを検証
final class NotificationSenderMock: NotificationSender {
var sendCallCount = 0
func send(_ message: String) { sendCallCount += 1 }
}
let mockSender = NotificationSenderMock()
let service = UserService(sender: mockSender)
service.notifyUser(id: 1)
// 振る舞い検証: send正確に 1回呼び出されたか
#expect(mockSender.sendCallCount == 1)
Stubは戻り値(結果)を確認し、Mockは呼び出し回数を確認するという違いがわかりますか。
この視点の違いが、テストダブルを理解する核心です。
では、SpyとFakeはいつ使うのでしょうか。
Spyは実際のオブジェクトをラップし、本物の動作はそのままにして、呼び出し履歴だけをひそかに記録する代役です。
「本物のロジックは残したいが、何回呼ばれたかも知りたい」というときに使います。
Swiftでは、実際の実装に委譲しながら、呼び出し引数と回数をプロパティに記録するSpyクラスを自作するのが代表的です。
Fakeは本物に近い動作をしますが、より軽量な実装です。
よくある例は、本物のDBの代わりに使うインメモリストレージや、Dictionaryで作った偽のリポジトリです。
本物のように動作しますが、本番利用には不十分な、まさにテスト用の実装です。
まとめると、次のように分けられます。
- 値だけ必要 → Stub
- 呼び出されたか検証したい → Mock
- 本物の動作+呼び出し記録の両方 → Spy
- 本物のように動く軽量な実装が必要 → Fake
よくある質問
Q. MockとStubは、実務では混ぜて使ってもよいですか。
実務では、1つの自作テストダブルクラスが値を返すStubと呼び出しを記録するMockを兼ねることが多く、境界は曖昧です。ただし、「値を与える目的」なのか「呼び出しを検証する目的」なのかを区別すると、テストの意図が明確になります。
Q. Mockを使いすぎるのはよくないと聞きました。
振る舞い検証を多用すると、内部実装が少し変わっただけでテストが次々に壊れます。そのため、可能なら状態検証(Stub・Fake)を中心にし、本当に必要な協調関係にだけMockを使うことをおすすめします。
5種類をすべて暗記するより、「今ほしいのは値か、それとも呼び出しの検証か」と自分に問いかけてください。
その1つの質問だけで、どの代役を用意すべきか自然に答えが出ます。
今日テストコードを書いていてまた迷ったら、この記事を開き直してください。皆さんのテストがより堅牢になることを願っています。

