世界のやまさ

SEKAI NO YAMASA

デシジョンテーブルを使用してテストしてみよう #TddAdventJp

この記事はTDD Advent Calendar jp: 2012 : ATND の6日目です。

前日(5日目)は @ さんの 受託開発でTDDを導入するということ でした。

デシジョンテーブルを使ってテストしたい!

仙台で行われている、仙台ソフトウェアテスト勉強会デシジョンテーブルとSpockで試すTDD(ネタ) - pocketberserkerの爆走にインスパイアされ、テスト技法としてデシジョンテーブルを書いた後に、具体的にどのようにテストの自動化を行うか考えてみました。

今回は(も?)FizzBuzzをお題としたいと思います。

デシジョンテーブルとは?

デシジョン・テーブルを活用するを参照してください。以下引用します。

デシジョン・テーブル(ディシジョン・テーブル)は decision table、 つまりそのまま訳せば「決定表」です。 様々な事情・条件を考慮して、 何かを「決める」ときに、 それを表形式で表現する方法です。

SpecFlowを使用する

id:pocketberserker さんは Groovy で実践していらっしゃいますが、私は c#デシジョンテーブルを表現できないか試行錯誤してみました。テーブルの表現は Ruby on Rails の受け入れテストでよく使用される Cucumber でできそうです。 c# では Cucumber は残念ながら使用できませんが、同じDSL(Gherkin)を使用できる SpecFlow というツールで代用ができそうです。

環境設定

以下の環境で試しています。

  • Windows 8
  • Visual Studio 2012 Professional (Expressは拡張が入らないので非推奨)
  • c#

また、Visual Studio の「拡張機能と更新プログラム」から次の拡張をインストールしてください

  • SpecFlow

実践

Project Setup | SpecFlow とだいたい同じように進めますが NUnit ではなく MSTest を使用し、さらに Chaining Assertion を使用します。xUnit 系とは少々異なる記述となりますが、単純に好みの問題です。

SpecFlowプロジェクトの作成

  • Visual Studio にて c# のクラスライブラリを新規作成します。名前は「FizzBuzz.Specs」とします

f:id:nnasaki:20121201015902j:plain

  • Class1.cs は不要なので削除します

  • パッケージマネージャ(NuGet)にて下記コマンドを実施してライブラリをインストールします

    PM> Install-Package SpecFlow

    PM> Install-Package ChainingAssertion

  • 「新しい項目の追加」より「SpecFlow Feature File」を選択します。名前は「FizzBuzz.feature」とします

  • App.config を開いて、要素に以下を追記します。

feature と steps の作成

SpecFlow のテストは feature にテキスト形式で自然言語で記述し、steps にテストをプログラミング言語で記述します。具体的にはFizzBuzz.feature を次のように編集します

前提以降に書かれているのがデシジョンテーブルとなります。たとえば入力値が3ならば3の倍数ですので true となり、5の倍数ではないので false 。結果は"Fizz"と出力します。

ファイルを保存すると「FizzBuzz.feature.cs」というファイルが自動生成されます。このファイルは feature と steps の関連づけを行います。

次に、Generate Step Definitions を選択すると、steps を自動作成します。

ここで一度、FizzBuzz.feature で右クリックして、「Run SpecFlow Scenarios」を実行しましょう。すべてのテストが「Pending」となっているので失敗すること(レッド)を確認します。

f:id:nnasaki:20121204011948j:plain

自動生成した steps は日本語が混ざっているとメソッド名がおかしくなりますので、若干手直した結果が下記です。

説明上日本語メソッドが入り交じっていますがご容赦ください。

Decision クラスで feature に記載したカラムのデータ型を指定し、 Setup メソッドでテーブルをIEnumerableに変換しています。

Assert メソッドにてテストを実施しています。

この時点では FizzBuzz クラスは未作成のため、当然ビルドエラーとなります。

FizzBuzz クラスを作成する

ソリューションを右クリックし、追加 - 新しいプロジェクトの追加 で「クラスライブラリ」を追加します。名前はとりあえず「FizzBuzz」とします。このクラスに実装していきます。

本来ならば、ここからTDDで開発するべきなのでしょうが、今回は長くなってしまうのと、FizzBuzzはすでに語り尽くされていることなので割愛します。詳細は @biac さんの記事VB2010 Express + NUnit 2.5 で、 初めてのTDD Step by Step: TDD.NETがとてもわかりやすいので、そちらを参照ください。

今回はTDDを重ねた結果、以下コードができたこととします。

尚、コードが完成するまでの間、SpecFlowはずっと失敗したままです。

テストを実行する

テストが以下のように成功(グリーン)になりましたら、実装は完了です。

f:id:nnasaki:20121204012341j:plain

次のステップ

次の faeture に取りかかる前に一度リファクタリングを検討してください。レッド・グリーン・リファクタリングがTDDにおける黄金の回転です。

今回はFizzBuzzという軽い題目でしたが、応用編としてブラウザを自動操縦してテストをすることも可能です。詳細はMSDN マガジン: BDD 入門: SpecFlow と WatiN によるビヘイビア駆動開発を参照してください。

WatiN はブラウザを表示させて動作するので、Selenium と同様にスローテストとなります。Rails ですと Capybara という抽象化ライブラリを使用して、テストのカテゴリによって、headless browser(画面表示なしのブラウザなので高速)を使用するように切り替えが可能です。

しかし、残念ながら .NET では Capybara のような抽象化ライブラリはまだ一般的ではないようで、もしかしたら coypu を使用すればできるかもしれません(未検証)。この辺は、追々調査をしていきたいと思います。

おまけ(MSTest & Chaining Assertion を使用する場合)

SpecFlow を使用しなくても、似たような感じなことは単体テストでできます。Visual Studio単体テストでは MSTest がサポートされています。しかし、パラメタライズテストに対応していないため、先ほどのChaining Assertion を利用することで可能となります。以下にコードを記載しておきます。

見ての通り、テストの可読性は SpecFlow のほうが上ですが、ちょっとしたテストならこれでも十分な気はします。

まとめ

以上で、デシジョンテーブルを使用したテストのお話は終わりです。デシジョンテーブルは仕様の整理にも役立つとても良いテスト技法ですので、皆さん是非活用していただければと思います。

明日(7日目)は @ さんです。