- 家永さんのコメント
前回に関連した話で,私のやっていたプロジェクトで,モックを使ったテストが急に赤くなって,「何でかなぁ何でかなぁ。直すのいっぱいあるなぁ」という経験があります。
そうですね。
今「モックテスト」という言葉が出てきました。モックテスト,モックオブジェクトという技法は,『WEB+DB PRESS Vol.35』の特集1「実演! テスト駆動開発」でも少し説明しましたが,この場で詳しく説明しましょう。
モックオブジェクト技法とは
「モックオブジェクト」「モックテスト」とは,ニセモノのオブジェクト(モックオブジェクト)をテストに使う技法です。本物のコードや本物のクラスではなく,テスト用の偽者のオブジェクトを作成し,テストを書きやすく,実行しやすくします。
詳しくは後述しますが,具体的には,テスト対象である本物のクラスと,それと協調動作するオブジェクトの偽物を使います。偽者オブジェクトは,テストの中で,テストの内容に即した動きをします。
メリット1:協調オブジェクトの開発前でもテストができる
たとえば図のように,A,B,Cという登場人物(オブジェクト)がいたとします。Aは,B,Cと話し合います。話し合うというのは要するにメソッドの呼び出しですが,システム開発の言葉では「協調動作」「相互作用」「コラボレーション」などと言います。
モックオブジェクト技法
![画像]()
A,B,Cに本物のオブジェクトを使い,本物同士を話し合わせてテストをしましょうというのが,大きい視点の「資産価値の高いテスト」です。しかし,このテストを行うためには,A,B,Cのすべての実装が終わってなければなりません。ということは,A,B,Cのすべての開発が終わるまで,テストはグリーンにならないということです。
しかし,実際のプロジェクトでは,Bは3ヵ月先に開発予定ですとか,Cは別のチームが開発していますというような場合があります。そのような場合には,本物のオブジェクトを使った資産価値の高いテストがなかなかグリーンにならず,開発を回しづらい状況になります。
そのようなときに,モックオブジェクト技法を使います。
たとえば,今テストをしたいのはAの振る舞い,Aの機能だとします。そして,BやCはまだ存在しない,もしくはセットアップするのが面倒なオブジェクトであるとします。
今テストしたいのはBやCではないので,BやCはテスト用の偽者でも問題はありません。「Aから話しかけられたBはこういう返答をします」「Aから話しかけられたCはこういう返答をします」という,あらかじめ仕込んだ「やらせ」の動作をする偽物を差し込んで,テストの中で本物のマネをさせれば,Aを開発しやすくなります。これがモックオブジェクト技法の考え方です。
メリット2:対象を絞り込むことで,加速力が得られる
モックの動きというのは,仕様そのものなんですね。ですので,「相手(モック)がこうしてきた場合には,A(テスト対象)はこういう動き方をします」というような視点,つまりテストの視点を絞ることができます。純粋にAのみに対するテストが書ける,ということです。
資産価値が高いテストのスコープは,A,B,Cの3つをまとめてテストするので広くなります。対照的にモックオブジェクト技法は,スコープをテスト対象のAそのものに絞ることができます。
スコープを絞ることには利点があります。スコープを絞るのですから,当然モックオブジェクトを使ったテスト自体は,レッドからグリーンになるまでの時間が短くなります。グリーンになるまでの時間が短いと言うことは,フィードバックを早く得られるということです。フィードバックサイクルの時間を短くすることで,くるくると,どんどん開発を先に回していくことができます。
またグリーンになったテストとそのクラスは,SVNなどのリポジトリにコミットすることができます。リポジトリに頻繁にコミットすることで,各開発者間でコードの共有が進み,大幅なマージを回避することもできるので,プロジェクトを円滑に進められます。
モックオブジェクト技法は,開発に加速力をつける,弾みをつける,そんなイメージです。テスト駆動開発では,モックオブジェクト技法を使って開発初期の加速力を得るという方法を,よく選択します。
メリット3:作成が難しいオブジェクトや,作成が難しい状況をシミュレートできる<
繰り返しになりますが,モックオブジェクト技法とは,テスト用に作成する偽物でした。偽物にテスト用の振る舞いをさせることができるということは,「もしもこうなったら」という状況をシュミュレートできるということです。つまり,実際のシステムで再現するのは難しい例外系のテストを簡単に行うことができます。
たとえばネットワークエラーが起きた場合の振る舞いのテストは,ネットワーク通信を行うクラスのモックを作り,そのモックからネットワークエラーをわざと発生させることによって簡単に行うことができます。
デメリット:仕様変更
しかし,モックオブジェクト技法もいいところだけではありません。
モックオブジェクト技法を使ったテストと普通のテストの違いは,仕様変更のときに現れてきます。
たとえば本物のクラス同士でテストを行う結合テストのような,ユニットテストよりも広い粒度のテストの場合,本物のオブジェクト同士が話し合うので,仕様変更を行われた場合には,当然その仕様変更に則ったテスト結果になります。
でも,モックオブジェクト技法を使う場合には話が変わってきます。モックオブジェクト技法を使う場合には,プログラマが自分の考えで仕様をシュミレートします。すると,モックオブジェクトが真似する対象の仕様に変更が発生したとき,モックも仕様に合わせて変更しないと,テストで考えている仕様と現在の本当の仕様が食い違ってしまう可能性が出てきます。
例を挙げて具体的に
例を挙げて,もう少し具体的に説明しましょう。
あるプロジェクトで,モックオブジェクト技法を使って,一つ一つのテストの視点を絞って実装を各個撃破していきました。各クラスを個別に開発したあとに,実際の本物のオブジェクト同士を話し合わせるテストもようやくグリーンになり,まず最初の段階はうまくいきました。
あるとき,お客さまの「今度はこういう機能作ってほしい」というお話を受け,クラスA,B,CのうちCに仕様変更や機能追加が入り,Cが全然違う形に変更され,Cの動きも変わってしまいました。
Cの動きが変わったので,本物を話し合わせるテストは,何もしなくても動きが変わります。しかしモックテストで作成したCの偽者自体は,モックのコードを手で直さない限り動きは変わりません。
本物のCの変更に合わせて,Aもコードを変更しなければならないというときに,本物を使う大きい視点のテストならば,Cの仕様変更に応じてAのコードを変更すれば,Aも動くようになります。
しかしAのみを対象にした小さいモックテストはどうでしょうか。AのテストにはCの偽物が使われています。もしもAを仕様に合わせて変更したとしても,Cの偽物を仕様変更に合わせて変更してはいないので,Cの偽者が前提だった小さいテストは全滅してしまうのです。
Aのモックテストが動くための前提が変わってしまったということです。「Cの偽者のメンテナンスをいくつやらなければならないんだろう」というような状況になってしまいます。
もう一度,テストの資産価値
そういうときには,前回のテストの資産価値の話を思い出しましょう。「大きい視点のテストはもうできあがっていて,かつそのテストがグリーンになっているので,モックオブジェクトベースの小さいテスト群はもう役割を終えたのではないだろうか」と,考えます。
Aをまず最初に開発するために,加速力をつけるためにモックオブジェクトベースで小さい視点のテストを行った。それらモックオブジェクトベースのテスト群は,もう役目を果たしたので,大きい視点のテストでレッドとグリーンのサイクルがまわせるようになったときには,小さい視点のテストはもういらないかもしれない。という考えが,テストの資産価値という考え方でしたね。
実際モックオブジェクトや小さいユニットテストの範囲で開発をまわしていくときには,仕様変更があってメンテナンス対象のインタフェースが変わったり,メソッド名が変わったときに,多量のコンパイルエラーが出てしまったりとか,たとえコンパイルエラーが出なかったとしても,レッドバーがたくさん出てしまう場面によく出会います。
大きいテストが成功しているとき,つまり本来必要な修正作業自体は成功しており,モックを本物に近づけるためのメンテナンスだけが残っているような場合には,テストとして価値の低いものは,強い理由がない場合には,消してしまうという選択も,メンテナンスコストを下げるために,プロジェクトをこれからまわしていくために,必要になってくるのではないかというのが私の意見です。