PhakeのモックでCakePHPをテスト コンポーネント篇
こんにちは。Phake のモックで CakePHP をテスト コントローラ篇・改では PHPUnit 標準のモック機構を(ほぼ)使わずPhakeフレームワークのみで CakePHP の Controller をテストする手法について述べました。
そのとき、Component も交えてのテストが可能だったため「コンポーネント篇」を書く予定は無くなったのですが、今回 単独の Component に対してテストする 必要に迫られたため改めて書くことにしました。
単独の Component のモック生成には一工夫必要
Controller と違い依存が少ないためModel のテストと同じように簡単だろうと思っていたらハマりました。
class MyComponent extends Component
{
public function __construct(ComponentCollection $collection, array $settings = array())
{
parent::__construct($collection, $settings);
$this->controller = $collection->getController();
return $this;
}
// 略
}
このような ComponentMyComponent
に対してテストを書きたいとします。
class MyComponentTest extends CakeTestCase
{
/**
* @return void
*/
public function setUp()
{
parent::setUp();
$this->any = Phake::anyParameters();
$this->mockCtrl = Phake::mock('AppController');
$collection = Phake::partialMock('ComponentCollection', $this->mockCtrl);
Phake::when($collection) ->getController() ->thenReturn($this->mockCtrl);
Phake::when($this->mockCtrl)->redirect($this->any)->thenReturn(null);
$this->MyComponent = new MyComponent($collection);
// 以下はテスト内容に応じて他のComponentを利用する際などに
$this->mockSession = Phake::mock('SessionComponent');
Phake::when($this->mockSession)->setFlash($this->any)->thenReturn(null);
$this->MyComponent->Session = $this->mockSession;
}
// 略
}
結論として、setUp()
はこうなります。
ComponentCollection のモック化がくせもの
Phake のモック生成にはPhake::mock()
というメソッドがあり通常はこれで問題ないのですが、まれにモックが生成できない場合があります。CakePHP といったライブラリが内部で特定のオブジェクトを要求する場合などです。
今回、ComponentCollection
はController
型のインスタンスを内部で要求しているためPhake::mock('ComponentCollection')
ではController
型のインスタンスが不足してしまいます。このため例外が発せられます。
Phake::partialMock()を使う
Phake にはPhake::partialMock()
というメソッドも用意されています。パーシャルモックはスパイとも呼ばれ、クラスの振る舞いを変えずに(Phake::When()->thenReturn()
などをせずに)回数や呼び出し順を検証するために使います。
このメソッドは引数を 2 つ受け付けます。Phake::partialMock($className, $value)
の形式で、$className
にはクラス名文字列、$value
にはコンストラクタに与える値やインスタンスを任意で渡します。
今回はComponentCollection
が要求するController
型のインスタンスを第 2 引数に渡すことで、モックが生成できるのです。テスト内では抽象 Controller で十分なため、AppController
のモックを渡しました。
$this->mockCtrl = Phake::mock('AppController');
$collection = Phake::partialMock('ComponentCollection', $this->mockCtrl);
そのため上記のようになります。
Component で他の Component を使う際
MyComponent
から CakePHP 標準のSessionComponent
を使いたい! そんな時は簡単。
$this->mockSession = Phake::mock('SessionComponent');
Phake::when($this->mockSession)->setFlash($this->any)->thenReturn(null);
$this->MyComponent->Session = $this->mockSession;
何も考えずにPhake::mock($className)
で生成できます。
Phake::mock()
とPhake::partialMock()
を使いこなすことで、あらゆる Class に対してテストが書けそうですね。PHP ではPhakeを使って快適モックライフを過ごしましょう!
Discussion