Static なメソッド呼び出しにモックを適用したい。でも PHPUnit や Mockery で static なモックを設定すると、他クラスで当該メソッドが読み込まれた場合にエラーが発生してしまう。。。
このようなジレンマを克服するために、PHP の OSS のモックフレームワークの Phake を使用して、上述の課題を克服する方法を説明します。
説明の前提条件
- モックを適用する対象は、
API
クラスのrequest()
メソッドとします。 -
request()
メソッドは、static とします。 -
request()
メソッドの引数には、任意の値を指定できることとします。 -
request()
メソッドは、基本的に以下のように呼び出されるものとします。
API::request($hoge);
-
API
クラスのrequest()
メソッドを使用している SUT(テスト対象)は、SUT
クラスとします。
1. SUT の修正手順
1) API
を、クラス変数として定義する
private static $api;
2) コンストラクタで、API
変数を初期化する
function __construct() {
self::$api = new API();
}
3) API
変数用の setter を用意する
public function set_API($arg) {
self::$api = $arg;
}
DI(Dependency Injection)の、特に setter injection の手法を採用しています。
これにより、テストケースで static なモックを設定することが可能になります。
4) request()
メソッドの呼び出し箇所を、API
変数を使用する方法に変更する
// self::$api::request() という表記は出来ない模様。
self::$api->request($hoge)
上記 2) と合わせ、モックをセットしていない場合(=通常呼び出し時)は、これまで通りAPI::request()
と同じ挙動をすることになります。
また、モックをセットしている場合は、セットしたモックが呼ばれることになります。
2. テストケースの作成手順
1) tearDown()
メソッドを定義し、Phake::resetStaticInfo()
を呼び出すようにする
public function tearDown() {
Phake::resetStaticInfo();
}
これにより、各テストケース実行の度に static なモックの設定がリセットされるため、他クラスで当該メソッドが読み込まれた場合にエラーが発生することを予防できます。
2) static なモックを定義する
$mock = Phake::mock('API');
// 引数が毎回異なる前提なので、Phake::anyParameters() メソッドを使用しています。
// また、戻り値の変数 $return は、予め定義してあるものとします。
Phake::whenStatic($mock)->request(Phake::anyParameters())
->thenReturn($return);
// request() メソッドがキチンと1回だけ呼び出せていることの検証です。
Phake::verifyStatic($mock, Phake::times(1))->request(Phake::anyParameters());
Phake の各メソッドの後ろに Static とつけることで、static なモックとして定義できます。
3) static なモックを SUT にセットする
$sut = new SUT();
$sut->set_API($mock);
// テスト対象メソッドを呼び出します。
$sut->xxx();
これで、SUT に先に設定した setter injection との合わせ技で、Phake で static なモックを「副作用」なく使用することができるようになりました。