PHPモッキングフレームワーク "Phake" を使ってみる

弊社techブログに記事を書いたものを転載します

http://tech.aainc.co.jp/archives/1756

## Phakeってなに?

これですね。
[Phake - PHP Mocking Framework](http://phake.digitalsandwich.com/docs/html/)

<p>Unitテストをするためのモックを作るためのフレームワークですね。PHP Unitを利用したUnit Testについては手前味噌ではありますが、Qiitaに書いた[PHPUnitの使い方まとめ](http://qiita.com/items/1176b43b548a8c467a59)と、このブログ内の[伊藤さんの各記事](http://tech.aainc.co.jp/archives/author/itoh)が参考になると思います。PHPUnit付属のモックはとても便利なのですが「痒いところまで絶妙に手が届かない」感じで、ちょっと悩ましかったので「もっと良いモック」は無いものか…… と色々試そうとしているところなのです。

## インストール

git cloneでいけます

```
git clone git://github.com/mlively/Phake.git phake
```

早速、コードを書いてみましょう。パスを通さないとイケないみたいですね。

```
<?php
// パスを通さないとダメ
set_include_path(get_include_path() . ':' . "./phake/src/");
require_once( 'Phake.php' );

class fuga { 
  public $_Values = array ();
  public function __set  ( $key, $val ) {
    $this->_Values[$key] = $val; 
  } 
  public function __get ( $key ) {
    return $this->_Values[$key]; 
  } 
  public function getFuga () {
    return 'fuga';
  }
}

$obj = \Phake::mock('fuga');
$obj->test = 1;
print $obj->test . "\n";       // 空
print $obj->getFuga () . "\n"; // 空
print $obj->getHoge () . "\n"; // こける
```


という感じで、無いメソッドは普通にやるとコケてくれるみたいですね。素直なクラス設計をしている人には嬉しい仕様ではないでしょうか。__set, __get は 動いてくれない模様です。PHPUnitのモックもそうですが、できれば__set, __getはオーバーライドして欲しくなかったのですが、ここは潰してくれた方が嬉しい人も多いのかも知れないですし文句は言えませんね。さて、では、__call が有るとどうなんでしょう?  

```
<?php
// パスを通さないとダメ
set_include_path(get_include_path() . ':' . "./phake/src/");
require_once( 'Phake.php' );

class fuga {
  protected $_Values = array ();
  public function __set  ( $key, $val ) {
    $this->_Values[$key] = $val;
  }
  public function __get ( $key ) {
    return $this->_Values[$key];
  }

  public function getFuga () {
    return 'fuga';
  }

  public function __call ( $name, $args ) {
    return $name . '|' . var_export ( $args, true );
  }
}


$obj = \Phake::mock('fuga');
$obj->test = 1;
print $obj->test . "\n";
print $obj->getFuga () . "\n";
print $obj->getHoge () . "\n";
var_dump ( get_class_methods ( $obj ) );
```

無いメソッド呼んでも死ななくなっちゃいましたね。動的にプロパティを定義してアクセスするようなクラスでは、__callを利用して setter, getter っぽくアクセスするようにすれば良いのですかね。と、ここまで考えて、ふと思いついて以下を試してみました

```
<?php
// パスを通さないとダメ
set_include_path(get_include_path() . ':' . "./phake/src/");
require_once( 'Phake.php' );

class fuga {
  public $_Values = array ();
  public function __set  ( $key, $val ) {
    $this->_Values[$key] = $val;
  }
  public function __get ( $key ) {
    return $this->_Values[$key];
  }
}
$obj = \Phake::mock('fuga');
\Phake::when ( $obj )->
  __get ( 'xyzzy' ) ->
  thenReturn( 'scame' );

var_dump ( $obj->xyzzy ); // scame
```

おお、期待通り! でも、出来たけど直感的ではないですね。

```
<?php
// パスを通さないとダメ
set_include_path(get_include_path() . ':' . "./phake/src/");
require_once( 'Phake.php' );

class fuga {
  public $_Values = array ();
  public function __set  ( $key, $val ) {
    $this->_Values[$key] = $val;
  }
  public function __get ( $key ) {
    return $this->_Values[$key];
  }

  public function getFuga () {
    return 'fuga';
  }

  public function __call ( $name, $args ) {
    return $name . '|' . var_export ( $args, true );
  }
}

$obj = \Phake::mock('fuga');

\Phake::when ( $obj )->
  getFuga ( 1, 2, 3, 4 ) ->
  thenReturn( 'ヽ(゜▽、゜)ノ' );

\Phake::when ( $obj )->
  getFuga ( (object) array ( 'a' => 'b', 'c' => 'd' ) ) ->
  thenReturn( '＼(^o^)／' );

\Phake::when ( $obj )->
  getFuga () ->
  thenReturn( '(^^)' );

\Phake::when ( $obj )->
  getName () ->
  thenReturn( '名前だよ' );

var_dump ( $obj->getFuga ( 1, 2, 3, 4 ) );
var_dump ( $obj->getFuga ( (object) array ( 'a' => 'b', 'c'  => 'd' ) ) );
// 定義していない
var_dump ( $obj->getFuga ( (object) array ( 'a' => 'b', 'c'  => 'e' ) ) );
var_dump ( $obj->getFuga ( ) );
// __call を利用したゲッター的な
var_dump ( $obj->getName ( ) );
```

定義していない引数で渡すとNULLが返ってきています。とりあえず、これだけ分かっていればあとはリファレンス見つつ、頭捻ればテストを書き始められそうです。次回までには、もうちょっと使い倒して詳細にブレイクダウンしてみようと思います。