gallu’s blog

エンジニアでゲーマーで講師で占い師なおいちゃんのブログです。

Phake使ってみる:準備変

躊躇も脈絡も無く、start。


まずはinstall。
gitで引っ張ってこれるので大変においちゃん好み。
適当なディレクトリにおいておきましょう。

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

今回は「phake」ってディレクトリにしておいた。素直というよりは単純。


まず「使える」ようにする。
インストールディレクトリん中のsrcにパスが通ってる必要があるっぽいので、最低限が、こんな感じ。

$dir = "./phake/src/";
set_include_path(get_include_path() . ':' . $dir);

require_once('Phake.php');

面倒だからdirは相対パスだけど、本当は絶対パスのほうがよいと思う。
MagicWeaponだと、configのlib_pathに追加する感じかねぇ。


これでもぉ使えるので、試してみる。

<?php

$dir = "./phake/src/";
set_include_path(get_include_path() . ':' . $dir);

require_once('Phake.php');


class hoge
{
}

$obj = \Phake::mock('hoge');
var_dump($obj);

うん動く。


先に実験「無いメソッドはどなるの?」。

<?php

$dir = "./phake/src/";
set_include_path(get_include_path() . ':' . $dir);

require_once('Phake.php');


class hoge
{
}

$obj = \Phake::mock('hoge');
//var_dump($obj);

$i = $obj->test_method();

ちゃんと「メソッドないズラ」的エラーがでる。
hogeクラスに」メソッド入れてみる。

<?php

$dir = "./phake/src/";
set_include_path(get_include_path() . ':' . $dir);

require_once('Phake.php');


class hoge
{
  public function test_method($i = 0) { return 1; }
}

$obj = \Phake::mock('hoge');
//var_dump($obj);

$i = $obj->test_method();
var_dump($i);

ちゃんと「NULL」がreturnされる。
つまり
・メソッドがなけりゃちゃんとエラーが出て
・メソッドがあると、デフォルトでPhakeが吸収する
ことが判明する。


つぎ。んぢゃ「挙動を指定」してみる。

<?php

$dir = "./phake/src/";
set_include_path(get_include_path() . ':' . $dir);

require_once('Phake.php');


class hoge
{
  public function test_method($i = 0) { return 1; }
}

$obj = \Phake::mock('hoge');
//var_dump($obj);

\Phake::when($obj)->test_method()->thenReturn(1111);

$i = $obj->test_method();
var_dump($i);

ちゃんと出る。


引数の指定とかを少しまとめて。

<?php

$dir = "./phake/src/";
set_include_path(get_include_path() . ':' . $dir);

require_once('Phake.php');

class hoge
{
  public function test_method($i = 0) { return 1; }
}

$obj = \Phake::mock('hoge');
//var_dump($obj);

\Phake::when($obj)->test_method()->thenReturn(1111);
\Phake::when($obj)->test_method(1)->thenReturn(1234);
\Phake::when($obj)->test_method(9)->thenReturn(9999);

$i = $obj->test_method(1);
var_dump($i);
$i = $obj->test_method(9);
var_dump($i);
$i = $obj->test_method();
var_dump($i);
$i = $obj->test_method(9);
var_dump($i);

結果

$ php t.php
int(1234)
int(9999)
int(1111)
int(9999)

すばらしい意図通り。


やっぱり興味のある2a問題

<?php

$dir = "./phake/src/";
set_include_path(get_include_path() . ':' . $dir);

require_once('Phake.php');


class hoge
{
  public function test_method($i = 0) { return 1; }
}

$obj = \Phake::mock('hoge');
//var_dump($obj);

\Phake::when($obj)->test_method(1)->thenReturn(1234);
\Phake::when($obj)->test_method("1")->thenReturn("1234");

$i = $obj->test_method(1);
var_dump($i);
$i = $obj->test_method("1");
var_dump($i);

結果

$php t.php
string(4) "1234"
string(4) "1234"

…へぇ。若干以外な気もする。ほんのりとはまりそうなので、一応注意。


追加で実験。

\Phake::when($obj)->test_method("1")->thenReturn("1234");
\Phake::when($obj)->test_method(1)->thenReturn(1234);

だと

int(1234)
int(1234)

になる。基本後出し有効か。
…をや? ってことは?

\Phake::when($obj)->test_method("01")->thenReturn("1234");
\Phake::when($obj)->test_method(1)->thenReturn(1234);

$i = $obj->test_method(1);
var_dump($i);
$i = $obj->test_method("01");
var_dump($i);

int(1234)
int(1234)

…違うか。ハッシュ配列にまつわる話かなぁ、って思ったんだが。


これだと?

\Phake::when($obj)->test_method("")->thenReturn("1234");
\Phake::when($obj)->test_method(0)->thenReturn(1234);

$i = $obj->test_method(0);
var_dump($i);
$i = $obj->test_method("");
var_dump($i);

int(1234)
int(1234)

…あや。
もちろんたぶん…

\Phake::when($obj)->test_method(0)->thenReturn(1234);
\Phake::when($obj)->test_method("")->thenReturn("1234");

$i = $obj->test_method(0);
var_dump($i);
$i = $obj->test_method("");
var_dump($i);

string(4) "1234"
string(4) "1234"

だよね。


え?
ってことは…

\Phake::when($obj)->test_method(22)->thenReturn(1234);
\Phake::when($obj)->test_method("22a")->thenReturn("1234");

$i = $obj->test_method(22);
var_dump($i);
$i = $obj->test_method("22a");
var_dump($i);

string(4) "1234"
string(4) "1234"

やぱり orz


もういっちょ。

\Phake::when($obj)->test_method(22)->thenReturn(1234);
\Phake::when($obj)->test_method("22a")->thenReturn("1234");

$i = $obj->test_method("22");
var_dump($i);
$i = $obj->test_method("22a");
var_dump($i);

int(1234)
string(4) "1234"

予想通り。


実装見てないけど。おそらく、単純に「引数」を内部的に==でチェックしてるんじゃないかなぁ?
んと…まず「引数はとにかく、配列にpushして全部積んである」前提。メソッドがcallされたタイミングで「今渡された引数と、積んである引数予定とをチェック」。beginぢゃなくてrbeginなのはまぁきっとなんかあるんだろう。
そうすると…
・予定している(積んでいる)引数:22, "22a"
の場合、callで22(int)だと
・"22a" == 22 → true
で、文字列が帰ってきちゃう。
一方、callを文字列にしておくと
・"22a" == "22" → false
・"22" == "22" → true
で、ちゃんと見分けがついてる。


たぶん、こんな風なのだろう、と予想。


2a問題って基本的に「片方が数値(int)だと発生する」から。最終的には「ちゃんと型まで見てほしい」んだけど、現状は「すべて文字列にしておく」のが、いったん、回避策になりそう。
…内部で自力で「変数の型に応じて処理を内部でスイッチする」、ポリモーフィズムな実装とかやってるとテスト大変そうだけど。
でもまぁ、SimpleTestでも回避策はあったから、たぶんなんかあるんじゃないかなぁ?
なかったらpull requestしろ、って話だ、たぶんw


とりあえず「準備は出来た」感じかなぁ。
次回は、もうちょっとあちらこちら、たたく予定。「おいちゃんが使う分」程度にレジュメをそろえたいなぁ。