PHPでBDD(Behavior Driven Development)する方法

 2011/02/10
このエントリーをはてなブックマークに追加

RubyであればRSpecやCucumberとか使って、むしろBDDしているケースの方が多いようだが、PHPでやっている事例はまったく聞かない。 とりあえずPHPでもBDDできることは確認できたので、その方法のご紹介。 ※実戦投入にはもうちょっと検証は必要。

BDDってなんだ?

BDDとはビヘイビア駆動開発(Behavior Driven Development)でテスト駆動開発から派生したもの。 テスト駆動開発とドメイン駆動設計を統合したようなイメージ。 対象における「振る舞い」や「制約条件」の検証のために、自然言語的な記述でテストコードを記述する。 スペックファーストで仕様を作ってから実装するという流れになる(コードを書く前に振る舞いを決める)

ということで、以下ではPHPでBDDを行う方法について解説する。

Behatのインストール

まずはPHP用のBDDツールであるBehatをインストールする。Behatのサイトはhttp://everzet.com/Behat/。 過去にはPHPSpecと呼ばれるツールも存在したが、既に開発は止まっているようである。 Behatのインストールは全てpearコマンドを使ってインストール可能だ。 なお、前提条件としてBehatの動作にはPHPUnitが必要である。

モジュールのインストール

sudo pear channel-discover pear.symfony-project.com
sudo pear channel-discover components.ez.no
sudo pear channel-discover pear.phpunit.de
sudo pear install phpunit/PHPUnit
sudo pear channel-discover pear.everzet.comsudo
sudo pear install everzet/behat-beta

※既にPHPUnitが導入済なら下の2行だけでOK

ディレクトリ構成

Behatでは決められたディレクトリにフィーチャーやステップ記述ファイルを配置する必要がある。

+ features
 +  steps 
 +  support

テストシナリオを記載するファイルはfeaturesディレクトリの直下に拡張子.featureとして配置する。(例:math.feature) テストのステップを記述したPHPスクリプトはstepsディレクトリに配置する。拡張子は.phpだ。 そしてsupportディレクトリには初期化処理等のPHPスクリプトを配置する。

初期化スクリプトの作成

とりあえず、support以下にenv.phpという名前で初期化スクリプトだけ作成しておこう。

< ?php
require_once 'PHPUnit/Autoload.php';
require_once 'PHPUnit/Framework/Assert/Functions.php';

if(!function_exists("assertEquals")) {
function assertEquals($expected, $actual)
{
    if ($expected != $actual) {
        throw new Exception("$expected is not equal to $actual.");
    }
}
}
?>

最初のPHPでBDD

以上を踏まえて、最初のBDDをやってみよう。 本当は単なるクラスではないほうが例としては良いかもしれないが、初めてということで。

期待する振る舞い

フィーチャー名:
 数値をずっと足していくことが出来る。

シナリオ:2つの数字をたします
 私は計算機に 50 を入力します
 さらに私は計算機に 70 を入力します
 合計を押します
 その結果画面には 120 が表示されます

フィーチャー記述ファイルの作成

こんな感じで記述する

Feature: 足し算
   間違いをさけるために2つの数字を自動で合計します。
   これはテストです。

   Scenario: 2つの数字をたします
     Given 私は計算機に 50 を入力します
       And 私は計算機に 70 を入力します
      When 合計を押します
      Then その結果画面には 120 が表示されます

ステップを作成

フィーチャーを実行するためにステップを作成する。 上記の例では以下のようになる。

< ?php
require_once __DIR__ . '/../classes/Calculator.php';

$steps->Given('/^私は計算機に (\d+) を入力します$/',
    function ($world, $arg1) {
        if (!$world->calc) {
            $world->calc = new Calc();
        }
        $world->calc->push($arg1);
    }
);

$steps->When('/^合計を押します$/',
    function ($world) {
        $world->calc->summarize();
    }
);

$steps->Then('/^その結果画面には (\d+) が表示されます$/',
    function ($world, $arg1) {
        assertEquals($arg1, $world->calc->get_total());
    }
);

?>

それから、Calcクラスの空の実装を作ろう。場所は適当で良いが、作成したファイルはステップを記述したスクリプトの中でrequire_onceしておくこと。

< ?php

class Calc {

    public function push($num) {
        throw new Exception('undefined');
    }

    public function summarize() {
        throw new Exception('undefined');
    }

    public function get_total() {
        throw new Exception('undefined');
    }
}
?>

では一度実行してみよう。 実行方法は

behat featureディレクトリ名

だ。第2引数を省略した場合は、featuresディレクトリが対象になる。

テストの対象となる実装は現時点では例外を投げて終了しているので、テストは失敗する。 あとはテストが通るように実装していけば良い。 実装してテストが通るようになれば以下のような表示になるはずだ。

使用感等

  • 日本語でシナリオが書けるという点で分かりやすいし、再利用性も高い。そして顧客に書いてもらうこともできるかもしれない。
  • Seleniumと組み合わせて受け入れテストを自動化するなんていう用途にも使えそう。
  • features/stepの中には、ルールの記述を行うが、ファイル名が異なっていても同名のルールは定義できないので注意が必要。namespaceみたいなのが欲しいかも。
  • CIに組み込めるかどうかは未確認。xmlさえ吐いてくれてばJenkins様によろしくお願いできる。
 2011/02/10
このエントリーをはてなブックマークに追加

サイト内検索


著作

寄稿

Latest post: