モックとは何か

アプリケーションのコードは何かしら自身ではコントロール不可能な存在に依存していることがあります。iOSアプリでは通信先のサーバーや端末のカメラなどが代表例です。これらはユニットテストを書くときに検証結果が不安定になる、どうやって検証すればいいかわからないという問題をもたらします。

サーバーであれば通信環境によって結果が異なる可能性があり、テスト結果に望まない失敗をもたらします。カメラであれば、どうやってカメラに正しく命令できたことが検証できるのでしょうか。

またそれだけではなく、例えばUIからアプリを作る場合に、まだ作られていない依存コンポーネントをモックすることで依存先のコンポーネントが完成する前にUIの確認、テストを可能にするという使い方もできます。

これらの問題を解決するためにモックは存在しています。モックはこれらのコンポーネントを置き換えることでテストに検証結果の安定とテスト可能な状態をもたらしてくれます。

モックがとても便利である一方でモックの利用方法についてはTDDにおいて古典学派、ロンドン学派と呼ばれるスタイルの違いがあり、意見が割れています。

意見の違いが生まれる原因としてモックを使うことでいい事だけではなく問題も発生することがわかっているからです。

このモックの使い所にまつわる問題をiOSアプリ開発においてどう使うと良いのか、私の意見を紹介して行きます。

意見が分かれているように私の主張においても絶対の正解ではありません。この発表内容を受けて皆さん自身はどう使うのがいいと考えるのか、ぜひ議論してより良いユニットテストを一緒に目指しましょう!

テストダブルの分類

モックという用語がライブラリも含め広く使われていますが、その厳密な定義は「xUnit Test Patterns: Refactoring Test Code」にて提唱したテストダブルがより厳密に分類したものになります。モックも含めたテストダブルを効果的に使うにはまずは分類の定義を知っておくことが重要だと考えます。なぜ重要かについては後述する「偽陽性と偽陰性に影響するテストダブル」にて説明します。

xUnit Test Patternsで定義されたテストダブルには5つの分類があります。

Dummy 何もせずコンパイルを通すためだけの最低限の存在
Stub 間接入力を任意の値に置き換えるコンポーネント
Spy 間接出力を記録するコンポーネント
Mock 間接出力を記録し、事前に設定した期待結果との検証を行うコンポーネント
Fake 実体ではないが実体と同じように動作するコンポーネント e.g. オンメモリDB

間接入力と間接出力の説明図

間接入力と間接出力の説明図

Spyはテスト対象(SUT, System under test)が依存コンポーネントを呼び出した際の回数や引数を記録します。

StubはSUTが呼び出す依存コンポーネントの戻り値を置き換えることで任意の値を返すことできます。

ただしSpyはStubを包含しているため、Spyが間接出力を置き換えても問題ありません。

テストダブルの関係図

テストダブルの関係図