2024-11-10
最近テストコードを書いていて、モックをどういう基準で使うか迷うことがあった。
モックに対しては大きく分けると2つの立場があり、それぞれclassicistとmockistと呼ばれている。両者の違いはテスト対象の周辺に対するモックの使用方針にあり、大雑把に言えばclassicistは必要にならない限りモックを使わず、mockistはテスト対象以外には原則mockを使うというものだ。
このスタンスの違いはテストによって何を検証するかにも差を生む。classicistの方針に沿ったテストは、テスト対象とテスト対象が依存するものの振る舞いも含めて検証する。一方、mockistの方針のテストはテスト対象のみの振る舞いを検証する。これらのテストは前者をsociable tests、後者をsolitary testsと呼ぶらしい(参考)。両者の違いを主義の違いではなく、テストの違いと捉えると、違いが理解しやすいと思う。
で、実際に両者のテストをどう使い分けていくのかが問題。
自分がテストコードを書く場合、基本的にはsociableなテストを書くこと最初に考えるようにしている。コードのレイヤーが上がってくると、必然的にsociableなテストでは必要な準備が重たくなるので、そのコストを払うのが適切ではないと判断した時はモックの使用を検討する(=solitaryなテストの考え方を取り入れる)。
初手でsolitaryなテストを避けるようにしているのは、テストが脆くなりやすいと考えているため。すべてをsolitaryなテストで書こうとすると、テストコードはコンポーネント間の取り決めに依存することとなり、それはつまり局所的な実装の仕方に依存することであり、結果的にはリファクタリングに対する耐性を失い、壊れやすいテストになる(ということが『単体テストの考え方/使い方』でも説明されていた)。そして、この傾向は責務を割れば割るほど強くなる。
逆に考えれば、コンポーネント間の取り決めが堅く安定しているなら、モックの弱みは出にくいとも言える(ここでの堅さは型の堅さではない)。システム間の連携インターフェースのように、簡単には変わらない/変えられないようなものはモックのデメリットが出にくいし、そのように扱うことがモックによるリファクタリング耐性の劣化を防ぐことに繋がる。
という言語化ができたので、明日からも安心してモックを使おう。限られたケースで。