# 第5章 ## モックとは | 大分類 | 中分類 | 小分類 | 備考 | |--------|--------|--------|------| | テストダブル | モック | モック | モックフレームワークが提供する | | テストダブル | モック | スパイ | 開発者自身が実装するモック | | テストダブル | スタブ | スタブ | 設定によって返す結果を変えられるダミー | | テストダブル | スタブ | ダミー | null, false などのハードコード値 | | テストダブル | スタブ | フェイク | まだ存在しない依存を置き換える | - モックは外部に向かうコミュニケーション(出力)を模倣/検証する場合に使う - 外部に向かうコミュニケーションとは > テスト対象システムが依存に対してその依存の状態を変えるために行う呼び出し - スタブは内部に向かうコミュニケーション(入力)を模倣する場合に使う - 内部に向かうコミュニケーションとは > テスト対象システムが依存からデータを取得するために行う呼び出し - スタブとのやり取りは **検証してはならない** - テストデータを提供しているだけなので - メール送信システムで例えると、 - テスト対象システム -- メール送信 -> モックメールサービス - テスト対象システム <- メール取得 -- スタブデータベース ## スタブはテストを壊れやすくする スタブは実装の詳細を知っているので。 - CQS(Command Query Separation, コマンドクエリ分離)原則いうところの、 - コマンド -> モック - 値を返さないので - 註: `function (...): void {...}` のメソッドはコマンドといえる。値は返さないが、副作用を持ちうる処理( `$this->prop = $arg;` など)を行っているため - クエリ -> スタブ - 値を返すだけなので ## 壊れやすいテストとは 「操作」または「状態」から実装の詳細が漏れ出ているもの。 - 操作の例は、 `User.name = User.normalizeName('aaaa......aaaa')` のようなメソッドを呼び出さなければいけない。など - この場合 `User.name = 'aaaa......aaaa'` だけで完結するようにするべきで、normalizeや不変条件に関する処理は隠蔽するべき - 判断の指針として、「テスト時に何回メソッドを呼び出しているか」が(不正確ながらも)有用 - > 理想とすべきAPIの設計は、いかなる目標であれ、1つの操作で目標を達成できるようにする - 状態の例は、`if (Order.status !== 'packing' && (Order.status === 'shipped' || Order.status === 'delivered')) {...}` のように、本当に欲しい情報以外のものが外部から参照できる状態になっている、など - この場合 `Order.isShipped()` などに隠蔽するべき - > 公開する操作と状態は最小限に抑えなくてはならない ## デトロイトvsロンドン ふたたび - 2章でみたようにモック化する対象や範囲が異なる - デトロイトはテストケース間で共有される依存に対してのみテストダブルを使う - ロンドンは不変依存(値オブジェクトや列挙型(Enum)などの、値が変わらないもの) **以外すべて** の依存に対してテストダブルを使う - 註: 前章でいうところの「偽陰性」が多発しそうで怖い ## ヘキサゴナルアーキテクチャにみるモックすべき対象 たとえばこんなシステムで、「在庫が十分にあるとき商品の購入が成功する」テスト > 1. 顧客(customer)が店(store)から商品(product)を購入(purchase)しようとする。 > 2. 店にある商品の数(quantity)が足りている場合、 > 1. 店にある商品の数を減らす。 > 2. 領収書(receipt)を顧客にメールで送る。 > 3. 購入処理が成功したことを呼び出し元に伝える。 - メールサービス(だけ)をモックにする - > メール・サービスとのコミュニケーションは常に同じ仕様に従うことが期待されているため - 註: IEmailGatewayをモックするから? - メールの内容を検証することで購入した商品数が正しいか検証できる - storeをモックするとどうなるか - 購入に連動して在庫を減算する処理をモックに持たせなければならない - 実装の詳細が漏れ出る - 購入処理が成功したかどうかはプロダクションコードが返す値を検証すればよい - 共有依存(≒プロセス外依存)はモックする - 共有依存 ... テストケース間で共有される依存 - データベースなど - プロセス外依存 ... テスト対象のシステムとは別のプロセスでホストされる依存 - データベース(!)、メールサービス、メッセージパス、など ### モックしない対象 テスト対象のアプリケーションから **のみ** アクセスされるプロセス外依存はモックしなくてよい。なぜならそれはもはやアプリケーションの(実装の)一部なので。 > テスト対象のアプリケーションを、プロセス外依存との仲介役として、常にクライアントとプロセス外依存とのあいだに配置することができれば、どのようなクライアントであってもそのプロセス外依存に直接アクセスできないようになります。そうすると、テスト対象のアプリケーションはプロセス外依存との後方互換を維持しなくてもよくなります。なぜなら、テスト対象のアプリケーションはプロセス外依存と共にデプロイできるようになるからです。こうなると、仮に、プロセス外依存とのコミュニケーションの仕様を変えたとしても、クライアントに影響が出ることはありません。そのため、このようなプロセス外依存とのコミュニケーションは実装の詳細として扱えるようになります。 > このようなプロセス外依存の例としてよくあるのがテスト対象のアプリケーションからしかアクセスされないデータベースです。もし、データベースに対してテスト対象のアプリケーションを除くすべてのアプリケーションからアクセスされることが決してないのであれば、テスト対象のアプリケーションとデータベースのコミュニケーションに関する仕様を(既存の機能を破綻させない限り)好きなように変えられることになります。つまり、このようなデータベースはクライアントから完全に隠されるため、まったく別のデータ・ストレージに置き換えたとしても、クライアントにその影響が出ることはないのです。