プロトコルベースのSwift依存性注入テスト
外部の依存関係(ファイルシステム、ネットワーク、iCloud)を小さく焦点を絞ったプロトコルとして抽象化することで、SwiftコードをテストしやすくするパターンI/Oなしの決定論的テストをサポートする。
起動条件
- ファイルシステム、ネットワーク、または外部APIにアクセスするSwiftコードを書く場合
- 実際の障害を起こさずにエラー処理パスをテストする必要がある場合
- 異なる環境(アプリ、テスト、SwiftUIプレビュー)で動作するモジュールを構築する場合
- Swift並行処理(Actor、Sendable)をサポートするテスト可能なアーキテクチャを設計する場合
コアパターン
1. 小さく焦点を絞ったプロトコルを定義する
各プロトコルは1つの外部関心事のみを処理する。
2. デフォルト(本番用)実装を作成する
3. テスト用のモック実装を作成する
4. デフォルトパラメーターで依存関係を注入する
本番コードはデフォルト値を使用し、テストはモックを注入する。
5. Swift Testingを使用してテストを書く
ベストプラクティス
- 単一責任:各プロトコルは1つの関心事を処理する——多くのメソッドを持つ「ゴッドプロトコル」を作らない
- Sendable 一貫性:プロトコルがActor境界をまたいで使用される場合に必要
- デフォルトパラメーター:本番コードは実際の実装をデフォルトで使用する。テストだけがモックを指定する必要がある
- エラーのモック:障害パスをテストするために設定可能なエラープロパティを持つモックを設計する
- 境界のみをモック:外部の依存関係(ファイルシステム、ネットワーク、API)をモックし、内部型はモックしない
避けるべきアンチパターン
- すべての外部アクセスをカバーする単一の大きなプロトコルを作成する
- 外部の依存関係を持たない内部型をモックする
- 適切な依存性注入の代わりに
#if DEBUG条件文を使用する - Actorと組み合わせて使用する際に
Sendable一貫性を忘れる - 過度な設計:型が外部の依存関係を持たない場合、プロトコルは必要ない
使用場面
- ファイルシステム、ネットワーク、または外部APIに触れるあらゆるSwiftコード
- 実際の環境では引き起こすことが難しいエラー処理パスをテストする場合
- アプリ、テスト、SwiftUIプレビューのコンテキストで動作するモジュールを構築する場合
- Swift並行処理(Actor、構造化並行処理)を採用したテスト可能なアーキテクチャが必要なアプリ


