テスト駆動開発

プログラミングの方法論の一つとして テスト駆動開発という手法があります。 これはとても変わった開発手法で 「プログラム本体よりもテストから作る」 という開発方式です。 テスト駆動開発を含む、eXtreme Programmingという手法の提唱者である Kent beckによるテスト駆動開発入門という本が詳しいです。 また、世の中にはテスト駆動開発をサポートする 通称、Xunitと呼ばれる単体テスト支援ツールがあります。  JavaならJUnit  C++ならcppunit  全.Net言語対象のNUnit  他にも有名な言語には大抵なんちゃらunitがあります  Xunit←かのMartin Fowler氏による 特に、JUnit(1997年生まれ)が テスト駆動開発の火付け役になったと言えると思います。 元々このサイトでは.Net版のNUnitの解説を予定してたのですが F#版であるFsUnitというのがあることを知ったため こちらについて調べてみました。 なお、NUnitの話は、Expert F#の(p537-542)と Foundations of F#の(p311-312)に載ってますのでご参考までに

FsUnit

追記 この情報は古くなっています 詳細は確認していませんが 現在FsUnitはNUnit用のテストケースを作るためのラッパーになっているようです FsUnitはRaymond W. Vernagus氏によって 開発されている関数型の仕様フレームワーク(specification framework)で MITライセンスにより配布されています。 上記サイトよりFsUnitのdllまたはソースコードを適当な場所にダウンロードし dllの場合は参照の追加(ページ下部参照) ソースコードならそのままプロジェクトに加えてしまえば 使うことが出来ます。 (現時点でソースは200行弱しかないため、 動作内容の理解には良いと思います) 以下、2009/04/09現在のバージョン(0.6.1)で動作確認しています。   まず最初に、例をあげてみます。
足し算検証プログラム
//Copyright (c) 2008, Raymond W. Vernagus 
//All rights reserved.
#light "off"
open FsUnit;;
let add x y=x+y;;
specs "足し算のテストケース" [
    spec "1+1=2" (add 1 1 |> should equal 2)
];;
printfn "%s" (Results.summary());;
このプログラムは足し算を行う関数addに対する テストケースを定義してテストを実行するプログラムです。 (テスト駆動開発的には、例えばlet add x y = ()としておいて まずはテストを作るのですが、そこら辺は割愛) これをVisual Studioで実行すると次のように表示されます
実行結果
1 passed.
0 failed.
0 erred.
続行するには何かキーを押してください . . .
これは1つのテストが実行されて、 1件テストにパス/0件テストに失敗/0件のエラー であったことを表しています。 これは大体次のような動作をしています 1:テスト仕様の定義   add 1 1 |> should equal 2 → 結果A(Spec型) 2:テストの実行   spec "コメント" 結果A → 結果B(string*Result型) 3:テスト結果をまとめる   specs "コメント" 結果Bのリスト → ()(unit型) この時副作用で、非公開の配列Cに結果が格納される 4:テスト結果の作成   Results.summary() → 配列Cの中身をカウントして、文字列にして返す 1:では、shouldとequalという見慣れないものが出てきますが shouldのほうは簡単で、実質、構文糖衣みたいな関数です。
shouldの定義
    let should f x = f x
なので、1:は次のように書き換えても同じ意味です
1:の書き直し
add 1 1 |> equal 2
//パイプライン演算子を取ればこうなる
//equal 2 (add 1 1)
ここで、equalというのは 2つの同じ型の変数を受け取り、Spec型の値を返す関数です。 これで、1:はSpec型の値を返すことがわかりました。 ここで、Spec型はインタフェースで、 テストを実行するCheckメソッド等を定義しています。 spec関数にコメント文字列とSpec型の値を渡せば 内部で自動的にCheckメソッドを呼び出してくれます なので、2:の時点ででテストが実行され、結果はResult型の変数に入ります。 Result型は次のように定義されています
Result型の定義
type Result =
    | Pass			//テスト成功
    | Fail of string	//テスト失敗
    | Error of string	//エラー発生
テストはいくつでも書けるので 3:では2:の結果のリスト(と、コメント文字列)を渡します。 specs関数は、呼び出すと FsUnitのprivateな配列に結果が格納される 副作用のある関数です。 そして、最後にResults.summary()関数を呼び出すことで 内部に格納された結果をカウントし テスト結果を文字列にして得ることが出来ます。 テストを増やしてみた例です。
複数ケースの例
//Copyright (c) 2008, Raymond W. Vernagus (R.Vernagus@gmail.com)
//All rights reserved.
#light "off"
open FsUnit;;
let add x y=x+y;;
specs "足し算のテストケース" [
    spec "1+1=2" (add 1 1 |> should equal 2);
    spec "2+3=5" (add 2 3 |> should equal 5);
    spec "1+1!=3" (add 1 1 |> should not' (equal 3));
    //これは失敗する。equalはオブジェクトの内容の等しさまでは見ない
    spec "add 1 = ((+)1)" (add 1 |> should equal ((+)1))
];;
printfn "%s" (Results.summary());;
これで基本的な動作がわかったので 他の機能についても見てみます。
bool値を扱う場合
true |> should be True;;
false |> should be False;;
//こんな風にすればResultが手軽にみれます
//true |> should be True |> spec "";;
//equalが結構万能なので、これでも良いような
//true |> should equal true;;
同様にして、 Empty(""に対応), Null(nullに対応), NullOrEmpty(""またはnullに対応) が使えます。
not'による結果の否定
true |> should not' (be False);;
//true |> should not' (equal false)
SameAsによる同じ参照かの判定
let a = {new obj() with member x.ToString()="a"};;
let b = {new obj() with member x.ToString()="a"};;
let c = a;;
a |> should be (SameAs b) |> spec "";;	//Fail
a |> should be (SameAs c) |> spec "";;	//Pass
containによる特定要素の判定
//シーケンスなら何でも判定出来る
["item"] |> should contain "item";;
[|"item"|] |> should contain "item";;
let oyatu = seq ["peran peran";"nerunerune"];;
oyatu |> should not' (contain "banana") |> spec "";;
haveによる要素数の判定
//特定要素の個数ではなく、要素数の判定をしている模様
> ["a";"b";"c";"a"] |> should have 2 "a" |> spec "";;
val it : string * Result = ("", Fail "Expected: 2 a¥nActual: 4 a")
例外は少しクセがある?
raise'による例外の判定
(fun () -> failwith "fail") |> should (raise'<FailureException>)
//これはコンパイルエラー
//(fun () -> 1/0) |> should (raise'<System.DivideByZeroException>)
(fun () -> 1/0|>ignore) |> should (raise'<System.DivideByZeroException>)
FsUnitのテストコードは 文章として読めるよう工夫されていて面白いですね