Blog

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

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

みなさんこんにちは。@ryuzeeです。

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
このエントリーをはてなブックマークに追加