ご挨拶
ご無沙汰しております。前回はPHP で Hello, Worldしまくっていた石田です。
Phakeってなに?
これですね。
Phake – PHP Mocking Framework
Unitテストをするためのモックを作るためのフレームワークですね。PHP Unitを利用したUnit Testについては手前味噌ではありますが、Qiitaに書いたPHPUnitの使い方まとめと、このブログ内の伊藤さんの各記事が参考になると思います。PHPUnit付属のモックはとても便利なのですが「痒いところまで絶妙に手が届かない」感じで、ちょっと悩ましかったので「もっと良いモック」は無いものか…… と色々試そうとしているところなのです。
インストール
git cloneでいけます
1 |
git clone git://github.com/mlively/Phake.git phake |
早速、コードを書いてみましょう。パスを通さないとイケないみたいですね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
<?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 が有るとどうなんでしょう?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
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 っぽくアクセスするようにすれば良いのですかね。と、ここまで考えて、ふと思いついて以下を試してみました
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
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 |
おお、期待通り! でも、出来たけど直感的ではないですね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
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が返ってきています。とりあえず、これだけ分かっていればあとはリファレンス見つつ、頭捻ればテストを書き始められそうです。次回までには、もうちょっと使い倒して詳細にブレイクダウンしてみようと思います。
なるべく他のメンバーは書かないであろう流行りとは程遠いところや、 枯れたキーワードについて書いていこうと思います。 Perl / Java / PHP 及び周辺技術に興味があります。