奇想曲 in C#

2010-10-26

BDD Framework "MSpec"について

少し気楽な話題も取り上げようと思う。


Behavior Driven Development(BDD;ビヘイビア駆動開発)は、Test Driven Development(TDD;テスト駆動開発)を自然に拡大させた概念であるとされている。TDDでテストを書いていて、ある時テストケースの集合がシステムの振る舞い(ビヘイビア)を定義する「仕様」に見えてくる、という体験があるなら、このことは納得しやすいと思う。一方で、いくらテストケースを集めても要求される完全な振る舞いの定義には至らない、という反論も可能である。この点についてはテストケースの書き方で決まる部分も大きいし、対象とするドメインの規模・性格も関係するが、それでもなお両者には本質的に埋めがたいギャップがあるように筆者には思える。


ただ、最低限いえることは、テストケースは振る舞いの定義の一部を構成しうる、ということであろう。せっかくテストケースを書くのだから、それを単に実装の品質向上だけに使うのではなく、要求定義から設計まで含めた関係者全体で情報共有できる形として維持管理できた方がよい、という点には大いに賛同できる。もちろん、そのためには記述言語が問題となる。テストケースにわかりやすい名前を付けている人も多いかと思うが、その際に実装者にしか意味をなさないような要素は排除しなければ情報共有には使えない(対象の粒度にもよるが)。さもないと、テストはテスト、仕様は仕様、という管理になってしまう。ではどのような形=言語を用いて振る舞いを記述すればよいか?


”machine.specification”(通称MSpec)というC#上のBDDフレームワークは、振る舞いの記述からできるだけノイズ要素を取り除くという方針で設計されている。ここでいうノイズ要素とは、読む人間にとって無関係なトークン(例えばブロックを表す括弧は実装者にとっては重要だが要求者にとってはほとんど意味を持っていない)のことを指す。

例えば次のようなコードがMSpecによる振る舞いの記述である(定義のみを示している。テストのための実装は含めていない)。この例はhttp://elegantcode.com/2010/02/19/getting-started-with-machine-specifications-mspec/から引用させていただいた。

using Machine.Specifications;

namespace MSpecSample
{
    [Subject("Making a customer preferred")]
    public class when_a_regular_customer_is_made_preferred
    {
        It should_mark_the_customer_as_preferred;
        It should_apply_a_ten_percent_discount_to_all_outstanding_orders;
    }
}

顧客を「優良顧客」にする、という振る舞いについて記述したものである。Itというのは、MSpecが定義するC#のdelegateであるが、命名の妙によりうまく英語として成立するようになっている。(これらはC#にとってはただのprivateなフィールド宣言である。)

MSpecは、このようなコードの集合からHTML/XMLの「仕様書」を自動生成する機能を持っている。とはいっても単にフレームワークの認識する要素を整形して出力するだけのものであるが。


C#のaliasを用いれば無理やり日本語にすることもできる。今度は実装部分(delegateの中身や環境設定)を含めている。ネイティブ言語でなければこのフレームワークの意味はかなり失われてしまうであろう。

using Machine.Specifications;
using 前提条件は = Machine.Specifications.Establish;
using 次の要因 = Machine.Specifications.Because;
using 結果として = Machine.Specifications.It;
using 後処理は = Machine.Specifications.Cleanup;

namespace MSpecSample
{
    [Subject("優良顧客の扱いに関して")]
    public class 通常顧客から優良顧客へ変更した時
    {
        private static Customer customer;
        private static Order order;
        private static decimal totalAmountWithoutDiscount;

        前提条件は 次の通り = () =>
        {
            order = new Order(new[] { new OrderItem(12), new OrderItem(16) });
            totalAmountWithoutDiscount = order.TotalAmount;

            customer = new Customer(new[] { order });
        };

        次の要因 の = () =>
                customer.MakePreferred();

        結果として 優良顧客属性がセットされなければならない = () =>
            customer.IsPreferred.ShouldBeTrue();

        結果として 現在の全注文に対して10パーセント割引が適用されなければならない = () =>
            order.TotalAmount.ShouldEqual(totalAmountWithoutDiscount * 90 / 100);
    }

訳語には異論もあろうかと思うし、このやり方ではソースファイル毎にaliasを書かなければならないから多分実用にはならないだろう。しかし、もしこのようなBDDのプラクティスが本当に意味のあることだとしたら、日本語に対応したフレームワークがあっても悪くないし、さらにそれがMSpecの流れを汲むものであるとしたら、大体このような感じのものになるのではないだろうか。

このような記述を、テストというドメイン、あるいはBehaviorの記述というドメインに対するDSL(Domain Specific Language)であるととらえる人もいるようであるが、Behaviorというものはやはりアプリケーションの属するドメインの種類により様々な性質を持つものであろうと思うし、それを生かすのが有用なDSLの設計ではないかという疑問が拭い切れない気がする。

とはいえ、いかにもAgile開発によって辿りついたpragmaticな手法という感じで、これはこれでツールとして手軽であり面白い。


参考のために、上記を元に生成したドキュメント(html)のイメージを示しておく。

f:id:toshi_m:20101026023643j:image


本記事ではMSpecのごく一部(見た目)にしか触れなかった。わかりやすい入門としては下記のBlogがお勧めである:


また、Fluent NHibernateなどが既にMSpecを使用して開発されている。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/toshi_m/20101026/1288029255
Connection: close