PHPUnit3で始めるユニットテスト

第4回 モックオブジェクトを使ったテスト

この記事を読むのに必要な時間:およそ 11.5 分

また,既存のクラスやインターフェースを継承・実装したモックオブジェクトを生成することもできます。この場合のPHPコードは

/**
 * Item.phpにはItemクラスもしくはItemインターフェースが定義してある
 */
include_once 'Item.php';
$mock = $this->getMock('Item', array(), array(), 'AnotherItem');

この場合,第4引数にモックオブジェクトの名前を指定していますので,AnotherItemオブジェクトが生成されることになります。なお,前述の通り,モックオブジェクトは指定したクラスのサブクラスになりますので,finalクラスを使ってモックオブジェクトを生成することはできません。また,ItemクラスもしくはItemインターフェースで定義されているメソッドは,モックオブジェクト側でオーバーライドされます。

PHPUnit3のモックオブジェクト機能は,オブジェクトを生成するだけではなく,その振る舞いも任意に変更することが可能です。モックオブジェクトのメソッドの振る舞いを変更するには,

  • 実行回数の制約を設ける
  • メソッド名を指定する
  • 具体的な振る舞いを記述する

を指定する必要があります。

1つ目の「実行回数の制約」には,たとえば「1回のみ呼び出される」「0回以上呼び出される」といったものを指定します。具体的には,PHPUnit_Framework_MockObject_Matcher_Invocationインターフェースを実装したクラスのインスタンスを指定します。具体的なクラスについては,PHPUnitポケットガイドを参照してください。

なお,指定した制約を満たさない場合,テストに失敗したと見なされます。

また,メソッドの振る舞いには,

  • メソッドの戻り値
  • メソッドが投げる例外

のいずれかが指定可能で,PHPUnit_Framework_MockObject_Stubインターフェースを継承したクラスです。具体的なクラスについては,PHPUnitポケットガイドを参照してください。

以下,サンプルコードになります。

<?php
class MockTest extends PHPUnit_Framework_TestCase
{
    public function testMock() {
                     :
        $mock = $this->getMock('Item', array('getName', 'getCode', 'getPrice'));

        /**
         * getNameメソッドは一度だけ呼び出され,文字列「item001」を返す
         */
        $mock->expects($this->once())
             ->method('getName')
             ->will($this->returnValue('item001'));

        /**
         * getCodeメソッドはゼロ回以上呼び出され,1回目の呼び出し時は「001」,
         * 2回目の呼び出し時は「002」,3回目の呼び出し時は「100」を返す。
         * なお,4回目以降はNULLを返す。
         */
        $mock->expects($this->any())
             ->method('getCode')
             ->will($this->onConsecutiveCalls('001', '002', '100'));

        /**
         * getNameメソッドは少なくとも一度は呼び出され,呼び出されると
         * RuntimeExceptionを投げる
         */
        $mock->expects($this->atLeastOnce())
             ->method('getPrice')
             ->will($this->throwException(new RuntimeException()));

                     :
    }
}

また,実行回数以外に,期待した振る舞いをするかをテストするための制約も用意されています。これらの制約は,willメソッドの代わりにwithメソッドを使用し,その引数として利用可能です。

以下,サンプルコードになります。

<?php
class MockTest extends PHPUnit_Framework_TestCase
{
    public function testMock() {
                     :
        $mock = $this->getMock('Item', array('setCode', 'setData'));

        /**
         * setCodeメソッドは第1引数にオブジェクト型,第2引数に42より
         * 大きな数を受け取る
         */
        $mock->expects($this->any())
             ->method('setCode')
             ->with('item001');  // ->with($this->equalsTo('item001')) と等価

        /**
         * setDataメソッドは第1引数に「001」,第2引数にオブジェクト型,
         * 第3引数に42より大きな数を受け取る
         */
        $mock->expects($this->any())
             ->method('setData')
             ->with('001', $this->isType('object'), $this->greaterThan(42));

                     :
    }
}

具体的な制約クラスについては,PHPUnitポケットガイドを参照してください。

著者プロフィール

下岡秀幸(しもおかひでゆき)

PHP関連の情報サイト「Do You PHP?」の管理人。PHP歴は長いが,あまり仕事で使ったことがないという噂がある。最近はα版のPEAR・PECLに手を出しては地雷を踏んでいることが多い。

URLhttp://www.doyouphp.jp/http://d.hatena.ne.jp/shimooka/

コメント

コメントの記入