はじめに
単体テストを書く際、テスト対象のコードが外部のデータベース、APIサーバー、ファイルシステムなどに依存していると、以下のような問題が発生する:
- テストの実行が遅い
- テスト環境の準備が複雑
- テスト結果が不安定(ネットワークエラーなど)
- 特定の状態やエラーケースの再現が困難
これらの問題を解決するために使われるのが**テストダブル(Test Double)**である。
テストダブルとは、テストにおいて依存先のコンポーネントを本物の代わりに置き換える「代役」のことである。映画のスタントダブルのように、本物の代わりにテスト専用の実装を使用する。
本記事では、テストダブルの5つの種類(Dummy、Stub、Fake、Spy、Mock)について、それぞれの目的と使い分けを、Goのコード例を交えて解説する。
テストダブルの基礎知識
テストダブルの5つの種類
テストダブルには5つの種類がある。それぞれ目的と使い方が異なる。
| 種類 | 目的 | 特徴 |
|---|---|---|
| Dummy | 引数を埋めるだけ | 実際には使用されない |
| Stub | 決まった値を返す | 状態検証に使用 |
| Fake | 簡易的な実装 | 実際に動作する軽量版 |
| Spy | 呼び出しを記録 | 履歴を後で検証 |
| Mock | 期待を事前設定 | 振る舞い検証に使用 |
前提:テスト対象のコード
以下の例では、データストアに依存するサービスをテストする。
テストダブルの種類と実装例
それぞれのテストダブルについて、具体的なコード例と使いどころを見ていく。
1. Dummy
Dummyは、引数を埋めるためだけに存在し、実際には使用されないオブジェクトである。
使用例
使いどころ
- 関数のシグネチャを満たすために引数が必要だが、実際には使われない場合
- 呼ばれたら即座に失敗させることで、誤用を検出できる
2. Stub
Stubは、呼び出しに対して決まった値を返すだけの単純な実装である。状態検証に使われる。
使用例
使いどころ
- テストで特定の返り値やエラーを返したい場合
- 状態(結果)を検証するテスト
- 最もシンプルで使いやすいテストダブル
3. Fake
Fakeは、実際に簡易的な動作をする軽量実装である。本物に近い振る舞いをするが、テスト用に簡略化されている。
使用例
使いどころ
- 複数のテストケースで共通のデータストアが必要な場合
- 実際の動作に近いテストが必要な場合
- 統合テストとユニットテストの中間レベルのテスト
- 例:メモリ内データベース、インメモリファイルシステム
4. Spy
Spyは、呼び出し履歴(引数、回数など)を記録し、後で検証することを目的とする。Mockとの違いは、Spyは事前に期待値を設定せず、実行後に履歴を確認する点である。
使用例
使いどころ
- メソッドが正しい引数で呼ばれたか確認したい場合
- 呼び出し回数や順序を検証したい場合
- ログ記録、通知送信などの副作用を持つ処理のテスト
5. Mock
Mockは、事前に期待(expectation)を設定し、テスト終了後にその期待が満たされたかを検証する。振る舞い検証に特化している。Spyとの違いは、Mockはテスト実行前に「こう呼ばれるべき」という期待を明示する点である。
使用例
使いどころ
- メソッドが期待通りの順序・引数で呼ばれたか厳密に検証したい場合
- 複雑な振る舞いの検証が必要な場合
- 外部サービスとのインタラクションをテストする場合
まとめ
テストダブルは、単体テストを高速で安定させ、テストしづらいコードをテスト可能にする強力なツールである。
- まず依存関係をインターフェースとして抽象化する
- 状態検証にはStub/Fakeを使う
- 振る舞い検証にはSpy/Mockを使う
- Mockは必要最小限に抑える
- テストダブルはシンプルに保つ
適切なテストダブルを選択することで、保守性が高く、リファクタリング耐性のあるテストを書くことができる。