テストダブルを整理する
テスト専用に用意される偽りの依存の総称をテストダブルと呼ぶ。 テストダブルにはモック、スパイ、スタブ、ダミー、フェイクがある。 これらは、大きく分けるとモックとスタブの2種類に分類できる。
- モック
- テスト対象から依存に向かって行われる外部へのコミュニケーション
- モック
- フレームワークを使用している
- スパイ
- 手書き
- スタブ
- モックの逆。依存からテスト対象に向かって行われる内部へのコミュニケーション
- スタブ
- シナリオごとに結果を変えられる
- ダミー
- シグネチャを満たすためだけのハードコーディングされる固定値
- フェイク
- 実装が存在しない依存のためのスタブ。UnimplementErrorを返すのが一般的
検証の必要性
検証(verify)はモックに対してのみ行う。(道具としてのモックを指す。すなわちモックとスパイの両方を指す)
スタブを検証しない理由はテストの最終的な結果を生み出すための一過程でしかなく、必要なデータを提供しているに過ぎないためである。 スタブを検証することは過剰検証であり、アンチ・パターンである。
これに対してモックの検証が必要である理由は、モックの検証はテストの最終的な結果に直結するためである。 例として、メールアドレスで会員登録をテストする場合を考える。
- 認証基盤との連携をモック(具体的にはモックorスパイ)して認証の初期状態を設定しておく
- 会員登録用のメールアドレスを入力して次のステップに進める
- 認証コードが送信されることを検証する
- 認証状態が認証コード入力待ちになることを確認する
1.で取り組んだことは前提条件の設定であり、最終的な結果を生み出す一過程でしかない。 これに対して3.で取り組んだことは期待する動作の検証、すなわち最終的な結果のテストになるため、検証が必要である。
おまけ
Flutterの状態管理パッケージとして有名なRiverpodのテストはProviderContainer#listenを用いて行われる。 この場合も依存に向かう外部へのコミュニケーションであるために、verifyで検証するのかなと勝手に思った(完全に個人的な想像ですが)
参考
Vladimir Khorikov著、須田智之訳、マイナビ出版「単体テストの考え方/使い方 プロジェクトの持続可能な成長を実現するための戦略」