10分モックオブジェクトを使ったテストPHPUnit3のモックオブジェクト生成機能CartTestクラスにモックオブジェクトを導入する今回はダミーのオブジェクト(モックオブジェクト)を使ったテストについて見ていきます。モックオブジェクトを使ったテストさて、折角完成したCartクラスですが、商品コードの代わりに商品クラス(Itemインターフェースを実装したクラス)を導入し、さらに商品の合計代金も取得できるようにすることになりました。具体的には、次のようなItemインターフェースが提供されています。商品コードのほかに、商品名や価格が取得可能なようです。getPrice();}しかし、肝心の実装クラス(ItemImplクラス)自身はまだ作成されていません。いった場合、どうすればいいでしょうか?ItemImplクラスが作成されるまで待ちますか?確かに、簡単なクラスであればそれもあり得るかも知れません。対象となっているクラスが複雑な場合や時間的な制限がある場合、そうも言っていられませんね。可能であれば、Cartクラスの修正やテストを進めておきたいところです。PHPUnit3には「モックオブジェクト」を生成する機能があります。「⁠モック(Mock⁠)⁠」には「模造品」「⁠偽の」といった意味がありますが、これを使うことで「まだ存在しない」クラスのインスタンスを使ったテストが可能になります。次に、PHPUnitで提供されているモックオブジェクトの生成機能について説明します。PHPUnit3のモックオブジェクト生成機能PHPUnitはPHPのリフレクション機能を使ってモックオブジェクトを生成します。生成されるオブジェクトにメソッドを定義したり、そのメソッドの振る舞いを指定することができます。「⁠制約」と呼ばれるクラス(PHPUnit_Framework_Constraintのサブクラス)を使って、期待する回数だけメソッドがコールされたかどうかや、期待した振る舞いをしているかどうかのテストも可能です。それでは、具体的な生成手順を見ていくことにします。モックオブジェクトの生成は、PHPUnit_Framework_TestCaseクラスのgetMockメソッドを呼び出すだけです。getMockメソッドのAPIとパラメータについては、以下のようになっています。モックオブジェクト自身のクラス名。デフォルトは、Mock_[$className]_[生成時刻(ミリ秒)のハッシュ値]最も簡単なコードは以下のようになります。$this->getMock('Item');このコードを実行すると内部では以下のようなコードが生成され、そのインスタンスが返されます。以下は、生成されるモックオブジェクトのソース例です。}}お気づきの通り、実際に生成されるオブジェクトは、$classNameで指定したクラスのサブクラスとなります。メソッドを指定してモックオブジェクトを生成する場合、getMockメソッドの第2引数にメソッド名を配列で指定します。'getPrice'));内部で生成されるコードは以下のようになり、第2引数で指定したメソッドが生成されていることが分かると思います。以下は、生成されるモックオブジェクトのソース例です。}}また、既存のクラスやインターフェースを継承・実装したモックオブジェクトを生成することもできます。'AnotherItem');この場合、第4引数にモックオブジェクトの名前を指定していますので、AnotherItemオブジェクトが生成されることになります。前述の通り、モックオブジェクトは指定したクラスのサブクラスになりますので、finalクラスを使ってモックオブジェクトを生成することはできません。ItemクラスもしくはItemインターフェースで定義されているメソッドは、モックオブジェクト側でオーバーライドされます。PHPUnit3のモックオブジェクト機能は、オブジェクトを生成するだけではなく、その振る舞いも任意に変更することが可能です。実行回数の制約を設けるメソッド名を指定する具体的な振る舞いを記述するを指定する必要があります。1つ目の「実行回数の制約」には、たとえば「1回のみ呼び出される」や「0回以上呼び出される」といったものを指定します。具体的には、PHPUnit_Framework_MockObject_Matcher_Invocationインターフェースを実装したクラスのインスタンスを指定します。具体的なクラスについては、PHPUnitポケットガイドを参照してください。一覧なお、指定した制約を満たさない場合、テストに失敗したと見なされます。メソッドの振る舞いには、メソッドの戻り値メソッドが投げる例外のいずれかが指定可能で、PHPUnit_Framework_MockObject_Stubインターフェースを継承したクラスです。具体的なクラスについては、PHPUnitポケットガイドを参照してください。以下、サンプルコードになります。2回目の呼び出し時は「002」、3回目の呼び出し時は「100」を返す。4回目以降はNULLを返す。}}また、実行回数以外に、期待した振る舞いをするかをテストするための制約も用意されています。これらの制約は、willメソッドの代わりにwithメソッドを使用し、その引数として利用可能です。以下、サンプルコードになります。}}具体的な制約クラスについては、PHPUnitポケットガイドを参照してください。利用可能な制約一覧CartTestクラスにモックオブジェクトを導入するそれでは、CartTestクラスにモックオブジェクトを導入してみましょう。前述の通り、次のようなItemインターフェースが提供されています。getPrice();}また、Itemインターフェースを導入することで、Cartクラスのaddメソッドの引数が変更になることとします。$amount)さらに、商品の合計代金を取得するgetTotalメソッドを追加します。getTotal()まずCartTestクラスから修正していきます。CartTestクラス内でCartクラスのaddメソッドを使用している箇所を見てみると、3種類の商品を扱っている事が分かりますので、3種類のモックオブジェクトを生成するようにしましょう。今回は、商品コード毎にモックオブジェクトを生成するprivateメソッドを追加しました。以下、CartTest.phpの抜粋です。}}そして、addメソッドの引数を、先のメソッドを呼び出すコードに変更します。1));また、Itemクラスを導入することで、Cartクラス内部に保持している商品情報の持たせ方も変更する必要がありそうです。第2キーが「object」の場合、値としてItemオブジェクトに変更します。具体的には、以下のようなハッシュとなります。$items['item_code_001']['object']これに伴い、テスト側も修正します。最終的なCartTestクラスは以下のようになりました。}}一方のCartクラス側ですが、getTotalメソッドを用意します。}}それでは、テストを実行してみましょう。6.$当然、テストに失敗します。それでは、Cartクラスを修正していきましょう。変更点は、以下のとおりです。修正後のCartクラスは、次のようになりました。}}再度テストを実行します。tests)$少し大きめの修正でしたが、すべてのテストに成功し、問題ないことが確認できました。次回は、PHPUnit3の便利な機能とPHP版プロジェクトビルドシステムであるPhingとの連携について見ていきます。Ltd.ページ内容の全部あるいは一部を無断で利用することを禁止します⁠。個別にライセンスが設定されている記事等はそのライセンスに従います。