JBehaveでBehaver Driven Developmentを体験してみる

Behaver Driven Developmentって何?

要するに、Domain Driven Designの「動くモデルをプロジェクトにおけるユビキタス言語にする」という発想と同じで、なんとか設計書、仕様書とテストケース、製品コードをばらばらに管理するのではなく、技術的詳細を隠蔽してビジネスユーザーと対話できるレベルにまで抽象化した「実行可能な仕様書」を作成することでコミュニケーションを円滑化し、ドキュメントや仕様と実際の成果物との乖離を防ぐという狙いがあるようです。

一枚の絵にするとだいたいこんな感じだと思われます。

「実行可能な仕様書」といっても、実際にプログラムとして実行可能なわけではなく、各ストーリーの一文=ステップに対応するテストケースプログラムを用意し、実行していくことになります。それだけただ単に各ステップに対応したテストケースを実行するだけになってしまいますが、そこは一工夫されています。SpringやJAX-RSからインスパイアされたのだと思われる「ステップから値をキャプチャする機能」があり、これを用いることで「○○の数は"10"個である」の「10個」の部分のような部分をテストケースへの入力としてキャプチャすることができます。

「複雑なAcceptance Testの自動化」というところに主眼が置かれていますが、この「実行可能な仕様書」の重要性は、CONTINUOUS DELIVERYでも「CHAPTER8 AUTOMATED ACCEPTANCE TESTING」で、丸々一章を割いて様々なテクニックと共に詳細に解説されています。JBehaveだけ見ているとあまりぴんと来ませんでしたが、プログラムで仕様を書いてJUnitから実行できる≒自動化できる
ということになりますね。

JUnitに組み込んで実行してみる

Github上に非常にシンプルなサンプルを置いておきました。
https://github.com/zyake/jbehave-hello-world

仕様書

仕様書は専用の文法にしたがって記述します。

calculation service stories

Scenario: calculates sum of two values (less than int max)

Given the system is ready
When I sum up the value "5" and the value "10"
Then the result should equal to 15

Scenario: calculates sum of two values (equal to int max)

Given the system is ready
When I sum up the value "1073741823" and the value "1073741824"
Then the result should equal to 2147483647

ステップの実装

仕様書にある各ステップの実装は以下のとおりになります。

public class CalculationSteps extends Steps {

    private Calculator calculator;

    private int result;

    @Given("the system is ready")
    public void theSystemIsReady() {
        this.calculator = new Calculator();
    }

    @When("I sum  up the value \"$var1\" and the value \"$var2\"")
    public void iSumVar1AndVar2(int var1, int var2) {
        result = calculator.sum(var1, var2);
    }

    @Then("the result should equal to $value")
    public void thenTheResultShouldEqualToTheValue(int value) {
        assertThat(result, is(value));
    }
}

テストケースクラス

仕様書とステップの実装をテストケースで関連付けして実行します。要注意なのは、JUnitから実行した場合、どのステップが実際に実行されたのか表示されないため、ステップ単位での実行結果を確認したい場合は、以下のライブラリを使用する必要があります。
https://github.com/codecentric/jbehave-junit-runner
使い方もシンプルで、実行対象のテストケースクラスに以下の1行を追加するだけです。
@RunWith(JUnitReportingRunner.class)

@RunWith(JUnitReportingRunner.class)
public class CalculationStoriesTest extends JUnitStories {

    @Override
    public InjectableStepsFactory stepsFactory() {
        return new InstanceStepsFactory(new MostUsefulConfiguration(), new CalculationSteps());
    }

    @Override
    protected List<String> storyPaths() {
        // デフォルト設定ではクラスパスから検索する。
        return Arrays.asList("calculation.story");
    }
}

実際にアプリケーションを開発する場合は、データの初期化方法、非同期の処理の待ち合わせ、開発途中のシステムのスタブや外接システム、ドライバの作成などが必要になり、Seleniumなどを使ってWeb画面と戦う必要があるかもしれませんし、相当の経験と技術力が必要で、一筋縄では行かないと思われます。

BDDの効果は?

結局、テストケースに相当するプログラムは自力で作らなければならないので、どういった効果があるのか疑問に思う方もいると思いますが、
個人的には「意識を変える」「仕様書との乖離を防ぎ、維持メンテ性を高める」ことには、かなり効果があるのではないかと思います。

例えば、結合試験の仕様書を作成し、そのとおりテストケースを実装したとしても、仕様変更やバグ修正の影響でパラメータやテストのステップが変わる、あるいは仕様書からのコピペミスや実装ミスで仕様書と乖離した試験が実施されることは極普通に起こりえます。

しかし、JBehaveを使って仕様書とテストケースの結合度を高めることができれば、「仕様書に書いてある試験が全く実施されていない」「パラメータが途中で変わってしまった」「テストケースはあるが、仕様書のどの記述に対応しているのかわからない」などといった自体を起きづらくすることはできます。また、仕様書は技術的な詳細が隠蔽されているので、テスターが途中でパラメータやステップの順序を変える、
あるいはテストケースの作成とテストケースの実装の分離、本来の目的であるビジネスユーザーと開発者の対話の促進といった効果はあると思います。

参考資料

JBehave公式
http://jbehave.org/
AiL – Simplest JBehave Scenario - Software is too expensive to build cheaply….
http://twasink.net/2012/06/25/ail-simplest-jbehave-scenario/
CONTINUOUS DELIVERY
http://www.amazon.com/dp/0321601912?tag=contindelive-20