この記事はFakeが正しいということを主張するものではなく、筆者のテストに求める役割がFakeと相性が良かったという私的な記事である
プログラミングのテストにおける Fake と Mock の違い。
- Fake は「実際に動作する簡略化された実装」
- Mock は「相互作用を検証するためのオブジェクト」
なぜテストに関する記事を読むと Mock に関してばかり書いてあるのだろうか。
Fake のほうが本物に近くすぐれていると感じた私の理解は足りないのだろうか。
そもそも、Fake の存在を知ったのはテストで Mock を実際に作ってから不便に感じ ChatGPT に聞いた時である。もっと Fake を早く知りたかった!
本記事は素人の振り返りメモです。
そのため、誤りがあればコメントにて訂正していただけるとありがたいです。
(この記事での Fake の方針は副作用を取り除き本番のコードをできる限り使用することである。)
比較表
項目 | Fake | Mock |
---|---|---|
主な用途 | 実際に動作する代替実装 | 呼び出し状況の検証(verify) |
実装 | 簡易な本物のようなロジックを含む | ロジックなし。ライブラリで生成されることが多い |
検証の可否 | 呼び出しの確認はできない |
verify() などで確認可能 |
実行時の挙動 | 実際に処理が行われる | 何も起こらない(設定した結果のみ返す) |
メリット | 本番に近い動作でテスト可能 | 振る舞いの検証が簡単・軽量 |
デメリット | 実装が必要・メンテコストがかかる | 動作確認には向かず、設定ミスに気づきにくい |
使用シーンの例 | メモリ上の DB・キャッシュのテスト | API 呼び出し回数・順番の検証 |
(ChatGPT の出力によるもの) |
Mock 本当に楽か?
ふーむ、互いの特徴はわかる。呼び出しの確認ができないのはデバッグ向きではないかもしれない。
だが、Mock の作成は本当に簡単なのだろうか?設定ミスをすることを考慮するとテストを作れば作るほどコストは増えていくことになる。
テストの際に、エラーが出ても Mock の設定ミスということも増えるかもしれない。
さらに深刻な問題になりそうなのが、Mock もそれを使用するテスト対象のクラスもどちらも間違っているがゆえにテストをパスしてしまう可能性だ。
対して、Fake ならば一度作ってしまえば関連するテストはいくらでも作ることができるということになる。
それに副作用を適切に分離してしまえば依存注入をするだけでコードには極力手をつけずに簡潔に記述することができる。(このような、機能は親に丸投げして依存や設定だけを差し替えるクラスのことをThin Wrapperというらしい。)
Mock でもテストはできるかもしれない。
でも、そのテスト、実際に意味ある?
- ロジック壊れててもテスト通る
- 値の中身バグってても通る
- Mock の設定ミスっても気づかない
そもそも何をテストしてるか曖昧
テストの行数はたくさんでも、中身はあまりない。
Mock の仮の振る舞いを記述して満足しちゃうし。
でも、本番のコードをテストしたいのに、Mock のせいで信頼性がないなら何のためのテストなのかが分からなくなってしまう。
一度作った Fake は、資産になる。
(各テストファイルに散らかっている Mock は、負債になる。)
たとえば:
- メモリ上で動く FakeDB → 本番 DB に近い挙動でテストできる
- FakeMailer → 本当に送ってはいないけど、送る内容や形式をちゃんとチェックできる
- FakeCache → キャッシュされるかどうかが実感としてわかる
しかも、一度作れば同じ Fake でいろんなテストに流用できる。再利用性、高すぎ。
それを使う上位層の Fake は依存注入の記述だけ。透明性、高すぎ。
テストファイルに書かないからテストのロジックだけをしっかり確認できる。可読性、高すぎ。
まとめ
今回、自分は Fake という存在を知りました。そしてその設計思想が今まで Mock で違和感を感じていた部分を解決してくれました。(Fake のような設計思想をクラシック派というようです。対して、Mock の設計思想はロンドン派というようです。自分はクラシック派に近い考えを持っていることが分かった。)
TDDのようなテストを書き換える前提で書く場合はMockのほうがいいかもしれませんが、私のテストに求める役割は品質保証でした。テストにもこのように複数種類あることを知らずに、Mockを紹介する記事ばかりを読んで、誤解をしてしまっていました。
目的と手段が一致していないことでこれまで違和感や不便さを感じていましたが、今回Fakeの存在を知り調べたことで一歩進めたような気がします。
まだまだ知らないことがありますが、学びを進めていきます。
Flutter で上位層の Fake(直接副作用を持たないロジック部分) を自動生成できる方法探し中。