前回の Java編に引き続きまして
今回も、テスト対象コードを変えずに、頑張ってテストを書くにはどうしたらいいのかを考えてみます。
// もちろん、リファクタリングをするべきですが、リファクタリングの足掛かりとして
// テストを先に書いておきたいという思いがあるのです。
PHPでも Mock というか、Test Double を楽しむための方法が幾つかありそうなのでまとめてみる。
オートロードが利用できるケース
symfony1.x の、 sfContext (Singletonパターン) に関わるテストで利用されている手段です。クラスの定義してあるファイルを自動的にロードするための機能spl_autoload_register() で、登録した関数は、すでにクラスが定義済みであった場合は呼ばれません。
これを利用して、全く同名のモッククラスを定義したファイルを用意しておき、テスト時に require_once などで、そのファイルを呼び出してやるわけです。これで、対象とするクラスの動きとしてはモッククラスの動きになります。
非常でお気軽な方法ですが、モック対象が頻繁に変更するようなケースなどではオススメできないかもしれません。
すべてのメソッドを網羅している必要はなく、騙すだけで良いならばこの方法はいいかもしれません。
テキトーなので、autoload.php は PSR-0 などに全然沿っておりませんのでご了承ください。 test1.php に関しては、オートロードで lib/Sample.php がロードされることはなく、先に定義した Sample クラスが利用されていることがわかります。
runkit
JMockit のように、メソッドの一部のみの挙動を変更したい!というニーズに答えるならば runkit になるかと思います。 php モジュールになってしまいますが、メソッドの内容を書き換えたり、定数を消したりとなかなか魔術を感じる便利なものです。 ただし、PHP5.3 では pecl からインストールができないので svn からソースコードを落とし、手動でコンパイルしてやる必要があります。
$ svn co https://svn.php.net/repository/pecl/runkit/trunk/ runkit
$ cd runkit
$ phpize
$ ./configure
$ make
$ sudo make install
以下は、クラスの定義を書き換えている様子です。
runkit + PHPMockFunction
runkit を利用して、かつ JMock のように使いやすくやろうじゃないか的なライブラリもあるのですが、
どうやらメンテされてません。誰かクールなのを作りませんか? 言いだしっぺですかそうですか。
PHPUnit の Mock
PHPUnit の Mock は、テスト可能なコードを書いていけば使い物になりますが、レガシーなコードのテストをまずは書いて進みたいという時には適さない様に思えます。
ちなみに、Symfony2 のコンポーネントは、この機能を利用しているため、テストもコードも非常に洗練されているような気がします。
【追記】次回予告
@hidenorigoto 先生にツッコミを受けたので、続編を書きます。mockery はどこまで出来るかわからないですけど、ドキュメント見た限り、なかなかクールでした!(ってか初めて知りました。)