PHPのスペックフレームワーク
Wednesday, May 16, 2012 02:05:22 AM
<!– more –>
PHPでのBDDについては、何度か取り上げてきましたがほとんどがストーリフレームワークであるBehatについてで、スペックフレームワークについてはあまりふれてきませんでした。せっかく今回はPHPカンファレンス関西のLTで2種類のBDDフレームワークについて話したので、スペックフレームワークについてまとめてみたいと思います。
最初にたどり着くのはPHPSpec
LTのスライドでも紹介したのはPHPといえばまず最初に思いつく PHPSpec です。大抵はまず「言語プレフィクス+Spec」で検索してみますよね。PHPはユニットテストがPHPUnitだからPHPSpecだよね、と誰もが連想できます。
で、PHPSpecの書き方としては、
class DescribeNewBowlingGame extends PHPSpecContext {
private $_bowling = null;
public function before() {
$this->_bowling = $this->spec(new Bowling);
}
public function itShouldScore0ForGutterGame() {
for ($i=1; $i<=20; $i++) {
// someone is really bad at bowling!
$this->_bowling->hit(0);
}
$this->_bowling->score->should->equal(0);
}
}
のように、まぁPHPのコードだとすれば読むのはそれほど困難ではないわけですが、やっぱりSpec(仕様)という視点で眺めると、ちょっと無駄な記述が目立ちますよね。それは言語の文法だからしょうがない、そうなんですが。
その他のスペックフレームワーク
で、試しにGitHubで他にないのかと探すと、意外とスペックフレームワークがある事に気がつきました。
名前所感
PHPSpec 最初に出てくる有名どころ
pecs JSpecに影響を受けている。しばらくメンテされていない…
spectrum まだα版でドキュメントもロシア語しかないが、設計の筋が良くいろいろできそう。今後に期待
speciphy yuya-takeyamaさん(@yuya_takeyama)がRspecに影響を受けて作成。subjectが使える。コアはPHPSpecを利用している
Spec for PHP Hamcrestライブラリとtokengetall関数を使って、自然に英語として読める文章として記述が可能。解析した結果をPHPUnitのテストケースとして生成して実行している
以下のコードは、Speciphy を例にPHPSpec以外でどのように書けるかをあらわしています。pecsもspectrumもほぼ同じような記述になります。
namespace SpeciphyDSL;
return
describe('Bowling',
describe('->score',
context('all gutter game',
subject(function () {
$bowling = new Bowling;
for ($i = 1; $i <= 20; $i++) {
$bowling->hit(0);
}
return $bowling;
}),
it('should equal 0', function ($bowling) {
$bowling->score->should->equal(0);
})
)
)
);
だいぶ読みやすくなりました。ではRSpecと比較してみま….す?えっ、やっちゃうの、それは….
describe Bowling, "#score" do
it "returns 0 for all gutter game" do
bowling = Bowling.new
20.times { bowling.hit(0) }
bowling.score.should eq(0)
end
end
こんな感じです。subjectやcontext使っている違いとかありますが、PHPの場合はどうしても無名関数を使うのに
function () {…}
を使わなくてはならないので、そこが惜しいのです。
変態PHPスペックフレームワーク Spec for PHP
そこで Spec for PHP ということなのですが、こいつはだいぶ変態です(ごめんなさい。良い意味です)。まず先程紹介したような funcition(){ … } の無名関数で書いたような記法も使えます。それはほぼ同じように書く事が可能です。で、何が変態かと言うと Hamcrestライブラリとtokengetall関数を使って構文解析することで、以下のようなSpecが書けちゃいます。
describe "Bowling"
describe "#score"
it "returns 0 for all gutter game"
$bowling = new Bowling;
for ($i = 1; $i <= 20; $i++) {
$bowling->hit(0);
}
$bowling->score should equal 0
end
end
end
it ブロックの内容は、Hamcrestを使って$bowling->score should equal 0 のように書いていますが、基本的にはPHPのソースコードであるとわかるでしょう。ただしdescribeやitなどの構文からはfunctionが消えて見通しが良くなっています。
では早速試してみましょう
Spec for PHP は以下の依存関係を持っています。
- PHP 5.3
- PHPUnit 3.5
- Hamcrest matchers library(ベータ版のときに作成されたらしい)
- PHP Object_Freezer 1.0.0
この記事の執筆時点で最新版の依存関係を適用すると、PHPUnitとHamcrestを利用している箇所で一部エラーになってしまいます。そこで、これらを修正し本家にPull Requestを送付しています。
このため、本家に取り込まれるまでは、私のforkリポジトリから取得することを推奨します。
https://github.com/sizuhiko/Spec-PHP
上記問題に対応するための修正と、日本語関連でPHP関数名の正規表現に x7f-xff の範囲を追加した修正が、 develop ブランチに入っていますので、こちらでお試しください。
まず必要なpearパッケージをインストールします。すでにインストール済みならスキップして構いません。
pear channel-discover pear.phpunit.de
pear install phpunit/PHPUnit
pear install phpunit/Object_Freezer
pear install Console_CommandLine
Install Hamcrest matchers library
pear channel-discover hamcrest.googlecode.com/svn/pear
pear install hamcrest/Hamcrest
つづいて、githubからcloneしてdevelopにスイッチします。
git clone git@github.com:sizuhiko/Spec-PHP.git
git checkout origin/develop -b develop
Spec for PHPはPHPUnit経由の実行方法と、Cliランナーも持っています。
- phpunit tests/AllTests.php
- ./spec4php.php tests
さいごに
Spec for PHP はテストコードを書いていて楽しい気分になれるような気がします。まだまだ奥がありそうなので、ちょっと追いかけてみようと思っています。また本家にPRが取り込まれたときなどに、インストール方法など更新予定です。
また、PHPのスペックフレームワークについて「もくもく会」か「勉強会」をやろうと思っていますので、こちらは@sizuhikoのTwitterなどをチェックしておいていただければと思います。