読者です 読者をやめる 読者になる 読者になる

知らないうちにphpspecがすごいことになっていた件

PHP bdd phpspec

昔あったphpspecが進化したものなのかそれとも別のものなのかは知りませんが。

すごい。軽くキモい。17分の動画です。

まず、いきなり「MovieCollectionにMovieをadd()するとcountが1になること」というのスペックを書きます。MovieCollectionクラスもMovieクラスもまだ作ってません。

f:id:iakio:20140306183705p:plain

phpspec runを実行すると当然失敗して、「MovieCollectionクラスが無いけど作ってほしい?」と尋ねてきます。Yなら空のMovieCollectionクラスが作られます。

f:id:iakio:20140306185158p:plain

関係ないけどこういうスクリーンキャストを作ったりライブコーディングする人は、ターミナルの色の設定したほうがいいですね。GitHubにいろいろカラーテーマがあるので。

その後いろいろ説明を省きますが、今度は「MovieCollection::add()メソッドが無いけど作る?」と言ってきたりします。ローカライズすると色々楽しめそうです(謎)。

f:id:iakio:20140306185434p:plain

また、引数で使っているMovieクラスの方は、空の実装だけあればphpspecが勝手にスタブを生成してくれる(newする必要すらない)というのも面白いところです。type hintを本来とは全然違う目的で使うというナウいPHPerに人気の手法ですね。

さらに進んで、MovieCollection#markAllAsWatched()メソッドのスペックを書くところ。

「ここではMovieのテストをしてるんじゃなく、MovieCollectionのテストをしているんだ」と、markAllAsWatched()がMovieの状態を変更することではなく、Movie#watch()メソッドが呼び出されることをモックを使ってテストしています。

<?php
class MovieCollectionSpec extends ObjectBehavior
{
...
    function it_can_mark_all_movies_as_watched(Movie $movie1, Movie $movie2)
    {
        $movie1->watch()->shouldBeCalled();
        $movie2->watch()->shouldBeCalled();

        $this->add([$movie1, $movie2]);

        $this->markAllAsWatched();
    }

簡潔ですね。Movieクラスに空のwatch()メソッドを作り、MovieCollection#markAllAsWatched()を実装することでこのテストは成功します。

スペックを書く対象は1つだけ、コラボレーターは全部Fake Object、というスタイルで書きやすさをつきつめてみたという感じです。

スタブやモックの使い方については、prophecyを見るのがよさそうです。

あと、PhpStormのリファクタリング機能の強力さもこの動画の見所です。