PHPSpecでユニットテスト

こんにちは、牧野です。
今回は、PHPでユニットテストを行う際に便利なユニットテスト用フレームワーク、PHPSpecの紹介です。
PHPSpecは、phpで振舞駆動開発ができるようにと作られたものです。
PHPSpecの詳細はこちら
http://dev.phpspec.org/manual/ja/
日本語マニュアルが大変充実しています。

振舞駆動開発について簡単に説明すると、テストありきの開発手法であるテスト駆動開発を発展させたような開発手法で、このプログラムはこんな動作をするべきだ、という要求仕様(スペック)を、そのままテストコードとして記述しながら開発を進めていく、という感じのものです。PHPSpecのような振舞駆動開発用フレームワークでは、要求仕様がすぐわかるようなテストコードの書き方をするようになっているので開発効率が上がるようです。

ではさっそく、まずはインストールについてですが、pearコマンドを使うとすごく簡単にインストールできます。
ルートユーザで


pear channel-discover pear.phpspec.org
pear install phpspec/PHPSpec

を実行すればOKです。

使い方、というか振舞駆動開発の流れですが、
1.要求仕様を文章に直し(it should ... の形式で)、それに沿ったテストコード(スペックファイル)を作成。
2.作ったスペックファイルで正しい結果が出てくるようなプログラムを作成。
3.スペックファイルを完成させてテストを実行して、正しい結果になることを確認。

1から3の繰り返しになります。
マニュアルにわかりやすい例がいろいろ載っているので、ぜひぜひそちらを見てみて下さい。

一応、やってみると、、、
MyStringクラスという、渡した文字列をどんどんつなげていくクラスを作ることにします。
まずはスペックファイルの作成です。

DescribeMyString.php


<?php
class DescribeMyString extends PHPSpec_Context {
  public function itShouldBeEmptyIfNoArgs() {
    $str = new MyString();
    $this->spec($str->body)->should->beNull();
  }
  public function itShouldBeCorrectString() {
    $str = new MyString('aaa');
    $str->add('bbb');
    $this->spec($str->body)->should->beEqualTo('aaabbb');
  }
}
?>

とりあえずこんな感じで要求仕様を表すスペックファイルを作ります。
ファイル名はDescribe~.phpに、クラス名はDescribe~にして、PHPSpec_Contextを継承させます。
メソッド名はit should ... の形式にします。これで、メソッド名を見ただけで何となく要求仕様がわかるようになります。
というか、要求仕様がわかるようなメソッド名をつけるようにします。…2つ目のメソッド名はあまりよくないかもしれません。

次に、実際にMyStringクラスを作ります。

MyString.php


<?php
class MyString {
  public $body;
  function MyString($string = null) {
    $this->body = $string;
  }
  function add($string) {
    $this->body .= $string;
  }
}
?>

クラスを実装したら、スペックファイルを少し直して、

DescribeMyString.php


<?php
require_once('MyString.php');
class DescribeMyString extends PHPSpec_Context {
  public function itShouldBeEmptyIfNoArgs() {
    $str = new MyString();
    $this->spec($str->body)->should->beNull();
  }
  public function itShouldBeCorrectString() {
    $str = new MyString('aaa');
    $str->add('bbb');
    $this->spec($str->body)->should->beEqualTo('aaabbb');
  }
}
?>

テストしてみます。


phpspec DescribeMyString

スペックファイルが複数ある場合は


phpspec -r

でまとめてテストできます。
-sをつけると、テスト結果が文章の形式で出力されます。メソッド名をちゃんとつけていないと、変な文章になります。。
うまくいくと、次のような出力結果が出ます。

あとは、さらに要求仕様を詰めてスペックファイルに追加、クラスの実装、テストを繰り返すことになります。

実際に初めて使ってみた率直な感想ですが、、、かなり面倒に感じました。。スペックファイルを作っている時に、何当たり前な事書いてるんだろう、という気がしてきます。
クラスの具体的な振舞はいくらでも考えられるので、細かく書こうとするとどこまでも細かく書けてしまって大変なことになります。

少しいいかも、と思ったのは、クラス内の実装を変更した時でした。phpspecコマンドを実行するだけで、とりあえず正しく変更できているかどうかがわかります。
慣れの問題もあると思うので、システムの規模が大きくなって多くのクラスを作らなければいけないような時は、けっこう使えるかもしれません。