モックとスタブの違い

"モックオブジェクト"という言葉は、テストのために本物のオブジェクトをまねる特殊なオブジェクトを表す言葉として定着した。しかしモックという言葉は元々スタブをキャッチーにしたものでなく、[スタブを使ったのとは別の] ユニットテスト方法を用いるためのものなのだ。この記事では、モックオブジェクトのファンに好まれる相互作用中心のテストと、よく行わている状態中心のテストスタイルとの違いを説明するために、モックとスタブの違いについて掘り下げる。

目次

  • 状態中心のテスト
  • 相互作用中心のテスト
    • EasyMockを使う
  • モックとスタブの違い
  • スタイルの違い
    • フィクスチャ*1のセットアップ
    • テストドリブン開発
    • テストの分離
    • 実装からテストを分離する
    • 設計スタイル
  • 結局一番良いのはどっちか?

"モックオブジェクト"という言葉に最初に出会ったのは数年前のXPコミュニティでだった。それから私はモックオブジェクトとどんどん出くわすようになった。その理由のいくらかはモックオブジェクのリーダー的開発者の多くがToughtWorksロンドンオフィスの同僚だったからだ。またいくらかは、XPに感化されたテスティングの文献でますます目にするようになったからだ。

しかしモックオブジェクトがちゃんと説明されていないことが多く、特にテスト環境で一般的に用いられるスタブと混同されているのを見かける。私も同じように考えていたことがあるから、混同されるのもわかる。しかしモック開発者とのやり取りのおかげで、徐々に私の固い頭も少しわかるようになってきた。

モックオブジェクトコミュニティで [学んだこと] の本質は、モッカー達が実践しているような別のテスト方法があるということだ。そしてXPの世界ではテストとデザインがとても絡み合っているために、[モックオブジェクトを使ったテストは]設計と開発についての[XPとは]異なる考えであることを暗示している。
モッカー達はよくこの違いを、状態中心のテストと相互作用中心のテストの違いとして引用する。この論文での私の目的は、この2つのテスト方法を明らかにし、その違いによって起きることを探ることだ。モックとスタブとの混同は、その違いの単なる表面的な現象にすぎないのだ。

*1:メインオブジェクトが使用するオブジェクト。「状態中心のテスト」の項参照

モックとスタブの違い

マーティン・ファウラーhttp://martinfowler.com/の以下のページを翻訳したものです。

Mocks Aren't Stubs モックはスタブではない

関連ページ

モックとスタブの違い 状態中心のテスト

状態中心のテスト

2つのスタイルを簡単な例で説明するところから始めようと思う(この例はJavaで書かれているが、どのオブジェクト指向言語でもこの原理は通用する)。オーダー[注文]オブジェクトを取得して、倉庫オブジェクトから注文を引き当てるようにしたい。オーダーオブジェクトはとてもシンプルで、一つの商品と一つの数量を持つだけだ。倉庫はいろいろな商品の在庫を持っている。あるオーダーに対して倉庫で引当をしようとする時、2つの対応が考えられる。もし倉庫にオーダーに引き当てるのに十分な商品があれば、オーダーは引き当てられて、その分倉庫の商品数は減る。もし倉庫に十分な商品がなければ、オーダーは成り立たないで、倉庫では何も起きない。

この2つの振る舞いは2つのテストが必要なことも意味していて、そのテストはJUnitでは非常によく行われるテストと似ている。

public class OrderStateTester extends TestCase {	
	private static String TALISKER = "talisker";
	private static String HIGHLAND_PARK = "Highland Park";
	private Warehouse warehouse = new WarehouseImpl();

	protected void setUp() throws Exception {
		warehouse.add(TALISKER, 50);
		warehouse.add(HIGHLAND_PARK, 25);
	}
	public void testOrderIsFilledIfEnoughInWarehouse() {
		Order order = new Order(TALISKER, 50);
		order.fill(warehouse);
		assertTrue(order.isFilled());
		assertEquals(0, warehouse.getInventory(TALISKER));
	}
	public void testOrderDoesNotRemoveIfNotEnough() {
		Order order = new Order(TALISKER, 51);
		order.fill(warehouse);
		assertFalse(order.isFilled());
		assertEquals(50, warehouse.getInventory(TALISKER));
	}
}

状態中心のテストは典型的なパターンで行われる。まずテストが必要なオブジェクトをまとめてセットアップする。これらのオブジェクトにはテストしているメインのオブジェクトも含まれているが、テストを行うためメインのオブジェクトが使う、その他のオブジェクトも含まれる。このオブジェクトのコレクション [=メインのオブジェクトが使うオブジェクトのまとまり] はフィクスチャ [付属品] と呼ばれる。上の例では、フィクスチャの中に1つだけ副次的なオブジェクトがあるだけでよかったが、普通はフィクスチャに非常にたくさんのオブジェクトを持つ。こういったフィクスチャはJUnitではsetUpメソッドの中でセットアップされることが多い。

フィクスチャのセットアップを行ったら、メインのオブジェクトをテストしたい処理を行うようにして起動する。処理が終わったら、今度は全てがうまくいったかを調べないといけない。複数のアサート文を使うことで調べられるこのこと[=うまくいっているということ]は、メインのオブジェクトとテストで注意を向けるサブオブジェクトに対して調べられる。上の例では、メインオブジェクトとしてorderを、サブオブジェクトとしてwarehouseオブジェクトを検査している。

メインのオブジェクトとサブオブジェクトの区別は、状態中心のテストではちょっと曖昧だ。しかし私は、メインオブジェクトは重点的にテストされるオブジェクト、サブオブジェクトはそれについてのテストが他にあり、今のテストでは正常に機能すると仮定されるオブジェクト、と考える。複数のメインオブジェクトがある場合があるだろうが、この論文での趣旨のため、単一のメインオブジェクトを扱うことにする。

モックとスタブの違い 相互作用中心のテスト

相互作用中心のテスト

今度は同じ振る舞いと使い方を相互作用中心のテストでやってみる。このコードでは、モックを定義するのにjMockライブラリを使っている。jMockJavaで作られたものでは比較的新しいライブラリである。

public class OrderInteractionTester extends MockObjectTestCase {
	private static String TALISKER = "Talisker";

	public void testFillingRemovesInventoryIfInStock() {
		//setup
		Order order = new Order(TALISKER, 50);
		Mock warehouse = new Mock(Warehouse.class);
		
		//expectations
		warehouse.expects(once()).method("hasInventory")
			.with(eq(TALISKER),eq(50))
			.will(returnValue(true));
		warehouse.expects(once()).method("remove")
			.with(eq(TALISKER), eq(50))
			.after("hasInventory");

		//execute
		order.fill((Warehouse) warehouse.proxy());
		
		//verify
		warehouse.verify();
		assertTrue(order.isFilled());
	}

	public void testFillingDoesNotRemoveIfNotEnoughInStock() {
		Order order = new Order(TALISKER, 51);		
		Mock warehouse = mock(Warehouse.class);
			
		warehouse.expects(once()).method("hasInventory")
			.withAnyArguments()
			.will(returnValue(false));

		order.fill((Warehouse) warehouse.proxy());

		assertFalse(order.isFilled());
	}
}

相互作用中心のテストには状態中心のテストと違うパターンがある。まず 1つ目のtestFillingRemovesInventoryIfInStock メソッドについてだけ考えよう。2つ目のテスト [testFillingDoesNotRemoveIfNotEnoughInStock()] では2つのショートカット [近道] をしているからだ [1つ目ではショートカットを使わない方法をまず説明する]。

フィクスチャののセットアップから始めるところがかなり違っている [上の状態中心のテストではオブジェクトの生成はsetUp()メソッドでまとめて行っていて、各メソッドでは生成処理を行わなかった]。メインオブジェクトについてだけ通常の [=テスト用でない] インスタンスを生成し、サブオブジェクトの通常のインスタンスは生成しない。そのかわり、モックインスタンスを生成する。

モックができたら、モックに想定 [expectation] を付け加える。この想定は、メインオブジェクトが動かされた時に、モックのどのメソッドが呼ばれるかを指定するものだ。想定はスタブとモックの一番の違いだ。

全ての想定が準備されたら、メインオブジェクトを動かすことができる。起動の後では、2つのことを行う。[1つ目は] 状態中心テストと同じようにメインオブジェクトに対してアサートをかける。一方で [2つ目に]、モックについても想定にしたがって呼び出しがされたかをチェックする。

これで状態中心テストと相互作用中心テストという名前が何に由来するかわかってもらえたと思う。状態中心テストではテストの結果を、メッセージング [原文ではstimulus{刺激};外部{から,へ}の刺激ということでメッセージングと訳す] の後で状態を調べることでチェックする。相互作用中心テストでは正しい相互作用がメッセージングによって起きたかをチェックする。もしメインオブジェクトの状態がメッセージングによって変わらなければ、アサートでは何もできず、モックを検証することで全てのことがチェックされる。

2つ目のテスト [testFillingDoesNotRemoveIfNotEnoughInStock()] では一つ目とは2つ違うことをしている。まずモックの生成の仕方が違う。コンストラクタではなくMockObjectTestCaseのmockメソッドを使って生成しているのだ。これはjMockライブラリに含まれる便利なメソッドで、これを使って生成されたモックオブジェクトはテストの最後で自動的に検証される [verified] ので、verify() を明示的に呼ぶ必要がないのだ。1つ目のテストでもこうすることができたが、モックを使ってどうテストするのかを説明するため、検証をより明示したかったので、そうしなかった。

訳者注:この次が相互作用中心テストの理解に重要だと思う

  • なぜ2つ目のテストで呼ばれるhasInventoryの引数がなんでもいいのかを理解する

2つ目のテストケースで違う点の2つ目は、withAnyArguments() を使うことで想定に対する制約を気にしなくていいことだ。こうする理由は、1つ目のテストでwarehouseオブジェクトに数字50が渡されることをチェックしているので、2つ目のテストでは同じ事を繰り返さなくてもいいからだ [渡された数字が50以外の数字かを判断しなくてもいい]。もし後でorderオブジェクトのロジックを変えないといけない [在庫があるケースのロジックが変わる or 在庫がないケースのロジックが変わる] 場合は、1つのテストだけが失敗するので、テスト変更の労力を少なくしてくれる。

EasyMockを使う

世の中には数多くのモックオブジェクトライブラリが存在する。よく聞くのはJava版も.NET版もあるEasyMockだ。EasyMockは相互作用中心のテストを促進するが、2つの点で論ずるに値するjMockとのスタイルでの違いがある。ここでまたお馴染みになったテストを挙げよう。

public class OrderEasyTester extends TestCase {
	private static String TALISKER = "Talisker";
	
	private MockControl warehouseControl;
	private Warehouse warehouseMock;
	
	public void setUp() {
		warehouseControl = MockControl.createControl(Warehouse.class);
		warehouseMock = (Warehouse) warehouseControl.getMock();		
	}

	public void testFillingRemovesInventoryIfInStock() {
		//setup
		Order order = new Order(TALISKER, 50);
		
		//expectations
		warehouseMock.hasInventory(TALISKER, 50);
		warehouseControl.setReturnValue(true);
		warehouseMock.remove(TALISKER, 50);
		warehouseControl.replay();

		//execute
		order.fill(warehouseMock);
		
		//verify
		warehouseControl.verify();
		assertTrue(order.isFilled());
	}

	public void testFillingDoesNotRemoveIfNotEnoughInStock() {
		Order order = new Order(TALISKER, 51);		

		warehouseMock.hasInventory(TALISKER, 51);
		warehouseControl.setReturnValue(false);
		warehouseControl.replay();

		order.fill((Warehouse) warehouseMock);

		assertFalse(order.isFilled());
		warehouseControl.verify();
	}
}

EasyMockは想定を設定するのに、record/replay [記録/再現] メタファを使っていて、まずモックで隠蔽したいオブジェクトのそれぞれに対してコントロールオブジェクトとモックオブジェクトを作る。モックオブジェクトの方だけでサブオブジェクトのインターフェースを満たし、コントロールオブジェクトは追加機能を提供する。想定を指定するには、予期される引数を引数にして、モックのメソッドを呼ぶ。戻り値がほしいなら、この後にコントロールへの呼び出しを行う。想定の設定を終えたら、コントロールのreplay()を呼ぶ。この時点でモックは記録 [recording] を完了してメインオブジェクト [からの呼び出し] に応答する準備が整う。そして [メインオブジェクトからの呼び出しがあった後] コントロールのverify()を呼び出す。

このrecord/replayメタファを初めて見て気後れする人たちがよくいるようだが、それにもすぐに慣れる。jMockがメソッド名の文字列でなくメソッドを指定しないといけないが、EasyMockはモックオブジェクトの実際のメソッドとして呼び出せるようにできる点で有利だ。これによってIDEのコード補完機能が使えるし、メソッドをリファクタリングすれば自動的に [IDEの機能で] テスト [コード中のメソッド呼び出し] が更新される。逆にEasyMockの弱い点は、jMockの [withAnyArgumentsのような] ルーズな制約をかけられないことだ。

モックとスタブの違い モックとスタブの違い

モックとスタブの違い

最初に述べたように、よくモックとスタブは混同される。モックを作るのに使っている各種ツールがスタブを作るのにも申し分なく使えるのだから、混同されるのもよく分かる。ただ、[どちらもツールでオブジェクトを作ることができるが、] 重要なのは、モックとは何か、スタブとは何か、ではなく、それぞれがどのように使われるか、ということだ。

スタブは主に生成したりコントロールしたりするのに手間がかかるオブジェクトをもみ消す [stub out] のに使われる。典型的なのはデータベース接続だろう。[手間のかかるオブジェクトを"もみ消す"ので、] 結果としてスタブを見かけるのは、ほとんどの場合、システムの外部との境界や、システム内の複雑なオブジェクトの塊のあたりだったりすることになる。スタブは、実際のオブジェクトと代替できるようインターフェースの実装をし、実際のメソッドをシンプルな [テスト用に] 準備されたデータを使うメソッドで置き換えることで作られる。

多くの [開発] グループでは、スタブが必要なサービスの全てに対してカスタムスタブを作っているが、数的に多くはないし、大抵複数のテストケースで使い回しされる。よって、汎用的なスタブ定義ライブラリを作る必要はない。とはいっても、スタブを作るのにモックライブラリを使うことはできる。モックライブラリはスタブ作成をちゃんとやってくれる。

テストでの役割からいって、特定のパラメータを渡すメソッドの呼び出しに対して、スタブは予め準備されたデータをただ返すような実装になる。モックのコミュニティの視点からいうと、モックが違うことでキーになるのは、モックのメソッドが呼ばれるテストでの、想定の設定の仕組みだ。モッキスト [=モック主義者] はよく”ただスタブ的に”値を返すようなモックオブジェクトについて述べている。そこで、[スタブと同じように値を返すだけのモックではなく、] テストの中にセットしたモックで想定をテストすることが、モックとスタブの違いを見る一つの方法となる。しかし結局のところ、この方法は [スタブとモックの違いを] ちょっと単純化しすぎている。私は、あるメソッドが呼ばれたらブーリアンの値をセットするといった感じで、想定をチェックするシンプルな手順を行うスタブを作っていたものだ [つまり、モックで行うことをスタブでもできる=モックとスタブの違いはそれほど明確でない]。 しかし、想定はスタブの主な特徴ではないが、モックにとっては最も重要な特徴であるといって差し支えないと考えている。

この記事のタイトル [Mocks Aren't Stubs] にもかかわらず、モックとスタブの違いは本当は一番の問題ではない。最も興味深いのは、相互作用スタイル対状態スタイルというところだ。相互作用中心のテストを行うテスターは全てのサブオブジェクトについてモックを作る。状態中心のテスターは、実際のオブジェクトを使うのが現実的でないものについてのみスタブを作る。例えば、外部サービスやコストのかかるもの、状態中心のやり方では扱いにくいキャッシュのようなものだ。

モックとスタブの違い スタイルによる違い

スタイルによる違い

どちらのスタイルも有利な点と付随的に処理することがある。どちらのスタイルを使えばいいのかを理解するには、考慮することがかなりある。

フィクスチャのセットアップ

状態中心のテストでは、メッセージに対する応答に関わる全てのオブジェクトを生成しないといけない。上の例では2つのオブジェクトしかなかったが、実際のテストでは多数のサブオブジェクトが関係してくることが多い。通常はテストが実行される度に生成され解放される。

しかし相互作用中心のテストでは、メインのオブジェクトと差し当たり必要なモックだけを生成すればいい。これは幾つもの必要なオブジェクトを揃える際の、手のかかる作業を多少回避してくれる。

実際には、状態ドリブンのテストをする人々は必要なオブジェクトを揃える部分をできるだけ再利用するように気を付けている。再利用を行うための一番簡単な方法は、オブジェクトのセットアップコードをxUnitのセットアップメソッドに入れることだ。幾つものテストで使われるより複雑なオブジェクトがある場合は、特別なオブジェクト生成クラスを作る。私はこのクラスを"オブジェクトマザー"*1と呼んでいる。これは初期のThoughtWorksのXPプロジェクトで使われた命名法によっている。大規模な状態中心テストではオブジェクトマザーは必要不可欠だが、オブジェクトマザーはメンテナンスが必要な付加的コードであり、どんな変更でもテスト全体に重要な影響を波及させる。また、フィクスチャのセットアップにかかるパフォーマンス上のコストのこともある。ただこれについては、適切に行っていれば、深刻な問題になったと聞いたことはない。ほとんどのフィクスチャオブジェクトは生成にコストがかからないし、コストのかかるものは普通スタブ化される。

結局私はどちらのスタイルも相手の方がやることが多いと言うのを聞いてきた。相互作用スタイルのテストを行う人はフィクスチャを作るのはとても手間がかかると言うし、状態スタイルのテストを行う人は、フィクスチャは使い回される、だがモックは全てのテスト毎に作らないといけない、と言う。

テストドリブン開発

モックオブジェクトはXPコミュニティから出てきたものだ。また、XPの主な特徴の一つは、テストドリブン開発を重要視していることだ。テストドリブン開発では、テストを書くことで推進される反復を通じてシステム設計も進んでいく。

そういったわけで、モックオブジェクトの支持者達が相互作用テストの設計における効果についてことさら語るのも驚くことではない。このスタイルでは、主要なオブジェクトに対する最初のテストを書くから [それらのオブジェクトの] 振る舞いの開発を始めることになる。サブオブジェクトについての想定を考えることで、メインオブジェクトとそれに関係するオブジェクトの間の相互作用を検証することになる。効果的なにメインオブジェクトの外向けのインターフェースをデザインしながらだ。いったん最初のテストを実行したら、後はモックに対する想定が、テストの次のステップと、それをどこからやればいいかを明確な指示を与えてくれる。

特に、このやり方は機能をアウト-インで実装していくように促す。まず下位のモックレイヤーを使ってユーザーインタフェースのプログラミングを始め、それから、下位のレイヤーについてテストを作りつつ、システムの一つのレイヤーを少しずつ [作り上げて] 進んで行く。

状態中心のテストはこれと同じようなガイダンスは与えてくれない。状態中心のテストを行う人達はしばしば関係するオブジェクトの集まりをまとめて手を打とうとする。少しずつ進んで行く同じようなやり方はできるのだ。まずフェイクメソッドを持ったサブオブジェクトを作り、それから実際のメソッドと置き換え、そしてそのサブオブジェクトのテストをすればいい。特にレイヤー単位で作業する、ということについていえば、状態中心のテストでは、まずドメインロジックを実装してからユーザーインターフェースを作るというミドル-アウトのスタイルがより一般的のようだ。

テストの分離

もし相互作用テストでシステムにバグを取り込んでしまっても、バグを含んだオブジェクトをメインオブジェクトとするテストだけが失敗することになる。しかし状態中心の方法では、バグオブジェクトをサブオブジェクトとして使うオブジェクトのテストも全て失敗することになる。つまり多用されるオブジェクトの一つの不具合がシステム全体のテストの失敗へと波及することになる。

このため、エラーの根本的な原因を見つけて直すのに、数多くのデバッグをおこなう結果になる。相互作用スタイルのテストを行う人々はこれを状態中心テストの重大な問題だと考える。しかし状態中心スタイルのテストを行う人々はこれを問題の根源とは言わない。大抵の場合どのテストが失敗するかを見れば悪いところは比較的簡単に見つけ出せるし、開発者なら他の不具合が根本の原因から起きていることがわかる。

テストの粒度はここで重要になることの一つだろう。状態中心のテストは複数のオブジェクトを用いるため、一つのテストでも、一つのオブジェクトに対してというより、いくつかのオブジェクトの集まりに対してのテストであるのが、状態中心のではメインであるのをよく見かける。もしその集まりが多くのオブジェクトにわたるものだとしたら、バグの本当の原因を見つけるのはずっと困難になることもある。実はこういう場合に起きているのは、テストの粒度が粗くなっていることなのだ。

相互作用中心のテストでこの問題に頭を悩ますことはあまりなさそうというのは、実際にありそうなことだ。なぜなら、メインオブジェクトを欺くように全てのオブジェクト [に対するモックオブジェクトなど] を作るのが相互作用中心のテストであり、そのことでちょうどいい粒度のテストがサブオブジェクトには必要だというのが明らかになるからだ。とは言うものの、あまりに粗いテストは必ずしもテクニックとしての状態中心テストの欠点ではなく、むしろ状態中心テストをきちんと行う際の失敗であるというのもまた事実だ。経験から言えるのは、それぞれのクラスに対して適度な粒度でテストを切り分けるように講じるのがいいとういことだ。幾つかのオブジェクトをまとめてテストすることも時には理に適うのだが、その時は6個未満の数個のオブジェクトに限られるべきだろう。さらに、粗すぎる粒度のテストのためにデバッグで問題があった時には、テストドリブンの方法でデバッグしていくべきで、そうしていれば、ちょうどいい粒度のテストが作られていくだろう。

基本的に、状態中心テストは単にユニットテストであるだけでなく、ミニ結合テストでもある。そのため多くの人達が、あるオブジェクトに対するテストが見逃すであろうエラーをクライアントテスト、特にクラスの相互作用が起きる箇所の精査によってキャッチすることもできるという事実を好む。[いろいろなパスを実行しながら網羅する方法に比べて] 相互作用テストはこの [相互作用のチェック] の質を下げる。その上、ユニットテストでグリーン [xUnit系のツールでエラーがなかったことを意味する色] になっても潜在的なエラーを見逃しているというリスクを冒すことにもなる。

現時点で私が強調しておきたいのは、どちらのテストスタイルを用いるにしても、システム全体を縦断して操作する受け入れテストも一緒に少し粗い粒度で行うべきだ、ということだ。

テストと実装との連動

相互作用テストを書く時には、メインオブジェクトが自身を使うオブジェクトに対して正しく反応するのを確かめるために、メインオブジェクトの外部呼び出しをテストしていることになる。状態中心のテストでは、どのように状態が導かれたかではないく、最終状態だけに関心が集められる。だから相互作用中心のテストの方がよりメソッドの実装に結びついている。サブオブジェクトの呼び出し方が変われば、状態中心テストが中断を起こすようになるだろう。

この連動によって2つ考えることが浮かぶ。一番重要なのはテストドリブン開発における効果だ。相互作用中心のテストでは、テストを書くことが振る舞いの結びつきを考えさせることになる。確かに相互作用テストをする人はこのことを利点と見ている。しかし状態中心のテストをする人は、外部インターフェースから何が起きるかのみ考えて、実装について考えるのはテストを書き終えるまで全て後回しにするのが重要だと考える。

実装の変更はテストを壊しやすい。だから、実装との結びつきは、状態中心のテストの妨げになる以上にリファクタリングの妨げにもなる。

相互作用中心のツールキットの性質のため、これがさらに悪化することもある。モックツールは大抵決められたメソッドの呼び出し方やパラメータの一致を設定する。呼び出しやパラメータがあるテストでは関係なくても、だ。jMockツールキットの目的の一つは、想定が影響ないところでは想定の設定を緩くできるようなフレキシブルさを持つことなのだ。

設計スタイル

この2つのテストスタイルで私が一番魅了される面は、どれくらい設計の決定に影響を及ぼすかということだ。以前私はスタイルが推奨するデザインの幾つかの違いに気付いた、と両方のテストスタイルの人に話したことがあるのだが、今では私はほんの表面を引っかいているにすぎないと確信している。

すでに [テストドリブン開発の項で] レイヤーへの取り組みでの違いのひとつについて述べた。相互作用中心のテストは、インタフェース作成 [原文:presentation(実演/提示)] から始める方法によって、外部をサポートする。メインモデルから離れたスタイルを好む開発者は状態中心テストを好む傾向にある。

もう少し小さなレベルでは、相互作用中心のテストをする人は、値を返すメソッドを少なくし、収集オブジェクトに作用するメソッドを支持する傾向がある。
例として、インフォメーション文字列を生成するのに、オブジェクト郡から情報を集める行いを挙げよう。通常やることは、各オブジェクトの文字列を返すメソッドを呼び出して、その結果の文字列を一時変数の中で [レポート文字列に] 組み上げるレポートメソッドを作ることだ。相互作用テストを行う人は、各オブジェクトにストリングバッファ [=文字列を格納するスペース] を渡し、各オブジェクトがそのバッファへそれぞれの文字列を追加するようにする。こうする時、ストリングバッファは収集パラメータとして扱われることになる [このパラメータの値がインフォメーション文字列となる]。

相互作用中心テストを行う人々は”列車事故”を回避することについて語りたがる。”列車事故”とはgetThis().getThat().getTheOther()のようなスタイルのメソッド連鎖のことだ。メソッド連鎖をしないようにすることは、デメテルの法則*2に従うこととして知られている。メソッドの連鎖の臭いがあるかぎり、フォワーディング [=転送] ・メソッドによって膨れあがった仲介オブジェクトの問題の臭いもすることになる。(デメテルの法則はデメテルの提案と呼ばれるほうがしっくりくるのじゃないかと私はいつも思うのだが)

人々にとってオブジェクト指向設計を理解するのが最も難しいことの一つが ”Tell, Don't Ask(告げろ、尋ねるな)” 原則 だ。この原則は、なにかをしようとするコードで、そのためにあるオブジェクトからデータをもぎ取ってくるのではなく、それをするようにあるオブジェクトに告げることを薦める。相互作用テストをする人は、相互作用テストがこの原則を促進することを手助けし、最近のコードにあまりにはびこっている getter紙吹雪 [=getterメソッドの多用] を避けるようにしてくれる。

*1:http://www.xpuniverse.com/2001/pdfs/Testing03.pdf

*2:オブジェクト指向の法則の一つ。他のオブジェクトから取得したオブジェクトへのアクセスをしないことで、クラス間の密結合を抑えようとする。