« [コラム] 単体テスト仕様書は不要になるか? | トップページ | [NEWS] NUnit 2.5.2 リリース、 次版からは公開場所が変更 »

2009年8月21日 (金)

[ブログ紹介] BDD の導入 - Dan North

BDD ( Behaviour-Driven Development : ふるまい駆動開発 ) の提唱者である Dan North 氏の記事の翻訳です。

Digital Romanticism - digitalsoul のブログ: BDD の導入 - Dan North
2009-08-19

この記事は Dan North 氏の記事 「Introducing BDD」 を氏の許可を得て翻訳した公式版 ("the official translation") です。 (原文公開日:2006年9月20日)

Dan North 氏は、 ThoughtWorks 社で働いていた 2003年頃から BDD を提唱されています。
※ 現在は ThoughtWorks 社に所属していないかもしれません。 当時所属していたことは、 O'Reilly の紹介ページ(英語) や sys-con.com の Dan North のページ(英語) の自己紹介欄などで判ります。

私自身は、 BDD ( というよりも、 NBehave を使って開発すること ) には懐疑的ですが、 BDD の考え方自体は示唆に富んでおり、 TDD の実践者はこの記事を読んでおくべきだと思います。

テストをなんと呼ぶべきかという質問の答えは簡単です。 これは関心対象となっている、 次に実装するふるまいを記述した文章です。

これは、 ほぼその通りだと思います。
100% ではないと思うのは… テストファーストで、 あるメソッドのテストを書き始めるとき、 そのテストメソッドでは、 これから作るメソッドの動的な外部仕様である、 ふるまいも記述しますが、 静的な外部仕様である、 メソッドのシグネチャ (メソッド名・引数・返値の型) をも決定するからです。 テストファーストでは、 テストを書くことで、 メソッドのシグネチャとふるまいの両方を定義しているのです。
※ ですから私は、 「ユニットテストはメソッドの外部設計である」 と思っています。

記事中のコードは Java ですが、 C# を使える人にはとくに苦になるところはないでしょう。 1点だけ…

public class ClientDetailsValidator {

  private final AgeCalculator ageCalc;

  public ClientDetailsValidator(AgeCalculator ageCalc) {
    this.ageCalc = ageCalc;
  }
}

このようにしてオブジェクト同士をつなぐ方法は、依存性注入("dependency injection")と呼ばれており

「AgeCalculator ってコンクリートクラスを受け取っておいて、 どこが DI だよ~!?」 と勘違いしないでください。 Java ではインターフェースの名前の頭に 'I' を付ける習慣が無いだけで、 この AgeCalculator はインターフェースのはずです。

記事では Java 用の BDD フレームワーク JBehave が紹介されています。 他の言語にも移植されていて、 .NET Framework 用として NBehave の開発が進められています。 ( 現在、 ver. 0.4 )
http://nbehave.org/ (英語)
http://www.codeplex.com/NBehave (英語)

ここで記事から離れて、 CodePlex に掲載された NBehave のサンプルを少し見ておきましょう。 まず、 ストーリーとシナリオを次のように記述します。

Story: Transfer to cash account

Narrative:
    As a savings account holder
    I want to transfer money from my savings account
    So that I can get cash easily from an ATM

    Scenario 1: Savings account is in credit
        Given my savings account balance is 100
            And my cash account balance is 10
        When I transfer 20 to cash account
        Then my savings account balance should be 80
            And my cash account balance should be 30

        Given my savings account balance is 400
            And my cash account balance is 100
        When I transfer 100 to cash account
        Then my savings account balance should be 300
            And my cash account balance should be 200

        Given my savings account balance is 500
            And my cash account balance is 20
        When I transfer 30 to cash account
        Then my savings account balance should be 470
            And my cash account balance should be 50

このあと 2通りのやり方があるそうですが、 そのうちのひとつ 「テキストファイルとアクションステップによる方法」 を見てみます。
シナリオごとにテキストファイルに保存し、 NBehave-Console.exe を使ってシナリオをコンパイルしておきます。
続いて、 アクションステップ (ActionSteps) クラスを記述します。 これは、 シナリオの各行を実際のクラス呼び出しに変換するメソッドの集まりです。
再び nbehave-console.exe を走らせると、 アクションステップを通じてシナリオを実行しようとします。 ( まだ実装クラスが出来ていないので、 失敗します。 )
このあとは、 実装クラスを書いてみては nbehave-console.exe を走らせることを繰り返して、 実装を完成させていきます。

考え方は理解できるのですが、 シナリオを書いてアクションステップを書くという手順は、 私にはどうにもまどろっこしいのです。 上に引用したシナリオは、 Visual Studio の単体テストとして書き下すならば、 私は次のように書くでしょう。

/// <summary>
/// Story: Transfer to cash account
/// Narrative:
///     As a savings account holder
///     I want to transfer money from my savings account
///     So that I can get cash easily from an ATM
///</summary>
[TestClass()]
public class AccountTest_TransferToCashAccount
{
    [TestMethod()]
    [Description("Scenario 1: Savings account is in credit")]
    public void SavingsAccountIsInCreditTest()
    {
        {
            // Given
            Account mySavings = new Account(100);
            Account myCash = new Account(10);

            // When
            mySavings.TransferTo(20, myCash);

            // Then
            Assert.AreEqual<decimal>(80, mySavings.Balance);
            Assert.AreEqual<decimal>(30, myCash.Balance);
        }

        {
            Account mySavings = new Account(400);
            Account myCash = new Account(100);

            mySavings.TransferTo(100, myCash);

            Assert.AreEqual<decimal>(300, mySavings.Balance);
            Assert.AreEqual<decimal>(200, myCash.Balance);
        }

        {
            // ・・・
        }
    }
}

このように、 直接テストコードとして表現できるものを、 わざわざ二度手間掛けて作業する必要があるのか、 私はそこを疑問に思うのです。

|

« [コラム] 単体テスト仕様書は不要になるか? | トップページ | [NEWS] NUnit 2.5.2 リリース、 次版からは公開場所が変更 »

ブログ紹介」カテゴリの記事

コメント

この記事へのコメントは終了しました。

トラックバック


この記事へのトラックバック一覧です: [ブログ紹介] BDD の導入 - Dan North:

« [コラム] 単体テスト仕様書は不要になるか? | トップページ | [NEWS] NUnit 2.5.2 リリース、 次版からは公開場所が変更 »