読者です 読者をやめる 読者になる 読者になる

Test Doubleを使うとテストの信頼性/保守性が下がるのか?

最近はユニットテストを行うテストコードにおいて、Test Doubleを多用してます。Test DoubleとはMockとStubを合わせた総称です。Test Doubleを用いると、信頼性や保守性が下がると言われることがあります。しかし、それはTest Doubleの用法を誤っているためではないかと僕は思うのです。Test Doubleの用途はユニットテストのスコープをテスト対象のクラスに限定するために用いるべきものです。実感として、Test Doubleを使っているからと言って、ユニットテストの信頼性/保守性が下がったとはあまり感じていません。

良く誤解されていますが、Test Doubleはスローテスト問題を解決するための手段ではありません。Test Doubleをうまく利用すれば、結果的にスローテスト問題を解決できます。しかし、スローテスト問題を解決するための手段ではないのです。スローテスト問題の手段としてTest Doubleを用いてしまうと、DBのセットアップが不要などの理由により、安易にファンクショナルテストやシステムテストなど、複数のクラスを串刺ししたテストにも用いようと考えます。そうしたより高いテスティングのレイヤにおいてTest Doubleを用いてしまうと、インタフェースの不整合がおき、想定していたデータと違うデータがTest Doubleから与えられてしまい、信頼性や保守性が下がってしまうのではないでしょうか。

ユニットテストにおいて、検証すべき事柄は、そのクラスに実装されているロジックが意図した通りに動作しているか、と言う事です。なので、相手にするスコープに限定してあれば十分です。UIのテストをしているのに、DBやファイルのセットアップが必要だとすれば、そのテストはユニットテストとしてのスコープを越えてしまっています。UIのユニットテストでは描画や操作に必要なオブジェクトのモックで十分テストができます。

クラスとクラスを串刺しにしたテストは、ユニットテストよりも上のレイヤーのテストで確認します。すなわちファンクショナルテストなり、全てのテストをまたいだ統合テストで確認します。それぞれのレイヤではユニットテストで行うほど、細かな観点でテストすべきではありません。いくつかのクラスを結合したテストですから、それぞれのオブジェクトのインタラクションを確認し、エンドツーエンドでの動作を確認することが目的です。

ところで、読みやすく、分かりやすいコードを書くには、実装時にクラスとAPIが担う責務を明確にし、それぞれを小さく保つことはもはや定石の一つと言ってよいでしょう。単一責務の原則も、1メソッド辺りの行数の指標も、変数のスコープを制限し、カプセル化しておくことも実は同じことを言っています。一度に考えなければならないことを減らすように実装をする事を指しているのです。
ユニットテストにおいても同じです。確認すべき事項を細かくあげ、一度に多くの事柄を相手にしない、というのは、一つ一つの確認において、複雑に考えなくてもよいようにするためなのです。

そう捉えてみると、Test Doubleの使いどころが見えてきます。テスト対象以外のクラスからの振る舞いに影響をできる限り受けないように実装するのです。そのためのMockであったり、Stubです。だから、ユニットテストでDBを直接操作するクラスのテストを書いている訳でもないのに、DBのセットアップを行うのは、ユニットテストの役割を考えると過剰な作業です。普段は起きないような例外が起きたとき、それがきちんと扱えているかチェックするテストコードをリグレッションテスト出来る方が重要です。

と言う事で、自分のTest Doubleに対する考えをまとめてみました。