先日(訳注:原文の掲載は2009/01/22)のTest-Driven Development(テスト駆動開発)Yahoo groupsでの議論(リンク)で関心を集めたのは、TDDに対するいわゆる「古典派」のアプローチと「モック派」のアプローチとのつながりについてだ。Steve FreemanやNat Pryce、Michael Feathers、Dale Emeryなど数多くの論客が、それぞれの用語の使い方と、自分自身のTDDへのアプローチについてを議論を重ねている。議論のなかでは、ほんとうに両者につながりがあるのだとしたら、つまるところ、両者を区別するものは一体何なのかといったことにも言及された。
「古典派TDDのアプローチのほうが良さそうに思えてきた」というOlof Bjarnasonの投稿(リンク)の投稿がきっかけとなって、70以上の投稿がグループに寄せられた。たとえば「以前に一度は、古典派からモック派に転向したのだとしたら、そのときに転向した要因は何だったのか?今のやり方についてどう思っているのか?」といったようにだ。投稿への初期の反応は、Olofの挑戦的な投稿のタイトルである「古典派とモック派」をめぐる議論が中心だった。さて、こうしたTDDアプローチの二元論にほんとうに意味はあるのだろうか?
Nat Pryce(リンク)(JMock(リンク)の作者のひとり)は、自身の思い(リンク)をこう表現している。
TDDのプラクティスをまるごと全部「モック派」と「状態ベース派」とに二分することに意味はないし、TDDを学んだり実践しようとしている人たちを混乱させるばかりで、何も益になるところはありません。
モックオブジェクトはただのツールです。TDDの実践で活用するツール群のひとつにすぎません。どんなツールでもそうですが、ツールというものは特定のコンテキストにおける問題を解決するために設計されるのです。コンテキストから外れた場面では、役に立たないどころか、むしろ邪魔になることもあります。
では今回の場合でいえば、どうすれば「ツール」を適用できるかどうかがわかるようになるのだろうか? 「ここではモックを使わずに状態を検証すべき」とか「ここではモックを使って振る舞いを検証すべき」といった判断を下せるようになるには何が必要なのだろうか?
両者の典型的な違いについて、Dale Emery(リンク)は次のような意見を投稿した。
多くの人はテストにおいて「状態ベース」と「振る舞いベース」とを分けて考えています。私は両者を取り混ぜて使っています。思うにこれは「結果」と「プロトコロル」のいずれを検証しているかの違いなのです。テストしたいことの本質が、テスト対象となるコードが正しい結果を返すことなのであれば、私はモックを使いません。いっぽう、オブジェクトが役割のプロトコル通りに正しく振る舞っていること――すなわち、与えられた条件下で、正しいコラボレータ(協調相手となるオブジェクト)に正しいメッセージを送っていることをテストしたいのであれば、私はテスト対象コードのコラボレータをモックオブジェクトにします。
Lior Friedmanはモックの目的はクラスの契約を表現することだと主張した(リンク)。
私のモックの使い方は、テスト対象となるクラスと、そのクラスが使うクラスとの間の「契約」を示すというものです。テストのゴールとは、こうしたクラス間の契約が満たされていることの検証なのです。
Charlie Poole(リンク) はこれを支援して、自分自身が状態ベースのテストを採用する場合の基準を述べた。
オブジェクトの振る舞いを検証する方法が無い場合に、私は状態ベースでオブジェクトをテストします。オブジェクトに対する「あらゆる」呼び出しがそのオブジェクトの状態を変えてしまうような場合です。個人的には、オブジェクトが何か仕事をしたと想定して、(モックを使って)それを検証するほうが好みです。
Adam Sroka(リンク)は彼がモックを使うのはどういった場合かについて自説を展開した。
私自身について言えば、所与のシステム境界(たとえばファイルシステムやネットワーク、データベースなど)では必ずモックを使っています。システムをトップダウンに捉えた場合のクライアントと、クライアントから使うインターフェイスとの間の結合を疎結合にしたい場合には、いつもモックを使っています。それから、テストで関心のあるオブジェクトが小さくて、フェイクしたりスタブ化することが簡単な場合に、私はモックを使うことはまずありません。
その後はスレッドが伸びていくにつれて、短いコメントが目立つようになり、議論もループしはじめたようだった。どうやら誰も「やることもあれば、やらないこともある」といった立場からさらに一歩踏み込んで、共通した分類を見出そうとはしていないようだった。さらにいえば、意見のそれぞれを(先述のSrokaによる「トップダウン」についての見解を除いて)まとめると、こういうことになる。すなわち、モックを使うかどうかの判断は設計によって導かれるのであって、よく言われているような「設計を導きだすためにモックを使う」という考えとは逆になっているのだ。
設計を導きだすためにモックを使うという考え方を示すために、Michael Feathersはこんな例を示した。
私に言わせれば、古典派/モック派というタイトルのほんとうの論点(リンク)とはつまり「求めるな、命じよ(Tell, Don't Ask)」を強調する立場との距離のとり方なのです。モックは「求めるな、命じよ」のアプローチを支援するためのツールです。
...
たとえば、あるオブジェクト(リンク)から、そのオブジェクトの持つエラー情報を取得したいとしましょう。
class Errors {
int errorCount();
Error getError(int index);
}
Errors errors = object.getErrors();
このコードは「求めて(ask)」います。このコードをテストするならErrorオブジェクトに対する状態ベースのテストを書くでしょう。これを「命じる(tell)」にするためには次のように変更します。
interface ErrorReceiver {
void accept(Error error);
}
ErrorReceiver receiver = ...;
object.reportErrors(receiver);
こうすることで、テストでは期待する振る舞いの設定されたErrorReceiverのモックを使い、プロダクトコードでは「本物の」ErrorReceiverの実装を使えるようになります。
その後の目立った反応としては、Steve Freeman(リンク)(JMockのもうひとりの作者)による投稿がある。この投稿では、彼の相棒であるNat Pryceによる「モックする価値のある協調オブジェクト」の3つの分類が紹介された(依存オブジェクト(Dependencies)、通知先オブジェクト(Notifications)、規程オブジェクト(Policies))。これに対して、Feathers(リンク)(それからColin Jack(リンク)も)は、JMockのふたりが提唱している設計アイデアは「設計を駆動するツールとしてのモック」という信条に基づいていると主張した。さらにいえば、一連の議論で「モック派のTDDへのアプローチ」について言及している場合、それはつまり設計の考え方を扱っているのだという。なので、こうした設計に対する考え方に具体的な名前をつけることで、「古典派TDD対モック派TDD」のような混乱が何度も繰り返しされる事態を避けられるだろうと提案している。
Pryceも投稿で言及しているように、彼とFreemanが執筆中の書籍(リンク)でこうした設計への考え方について何らかの分類が提供されることが要望されている。また、Pryceは自身のブログにState vs Interaction Based Testing(リンク)という記事もまとめている。
では実際問題、この議論はほんとうに新しくないばかりか、これまでに何度も繰り返されているものなのだろうか(Googleで検索してみよう)。「古典派TDD」であるということはすなわち「設計がモックをもたらす」のだろうか? それとも古典派TDDを特徴づける何か他の要素があるのだろうか? 「モック派TDD」を特徴づけているのは「モックが設計をもたらす」という信条なのだろうか? あるいは「求めるな、命じよ」だろうか? それともまったく異なる他の何かによるのだろうか? 何にしても、ほんとうにこれは「どちらかを選ぶ」といった類の問題なのだろうか? それとももっと「これはこれ、あれはあれ」といった完全な二択の問題なのだろうか?
もちろん、いつだってそうだが、この記事は単にYahoo group discussion(リンク)での議論の要点を(なるべく客観的であるように)取り上げたものであり、全体のうちのほんの一部分にすぎない。読者の皆さんも是非、自分自身でも実際に目を通してもらいたいし、関連リソースにもあたってみてほしい。そして、自分の経験から学んだことを、チームの他のメンバーと議論してみてほしい。
原文はこちらです:http://www.infoq.com/news/2009/01/classic-mockist-tdd