F# でEither型のテストを書く
F#でEither型を利用することはよくあることでしょう。特に例外処理関連で。
type Either<'T, 'U> = | Left of 'T | Right of 'U
さて、このEitherは色々な状況で使用できるのですが、F#でもっとも利用頻度が高い場面は例外処理のとき。
let divide x = function | y when y = 0 -> Left (ArgumentException "ばーん!") | y -> Right (x / y)
さて、これで処理が完了したのか例外が発生したのかが型で判断できる。
というわけでテストを書きましょう。
[<Scenario>] let ``割る数が0でなければRightに包まれた除算結果が返る`` () = Given (1, 1) ||> When divide |> It should equal (Right 1) |> Verify
正しい値かどうかのテストはこれでOK。では例外が発生する場合のテストは?
ということで単純に書こうとしても、以下の書き方では失敗する。
(* これだと失敗する *) [<Scenario>] let ``割る数が0であればLeftに包まれた例外が返る`` () = Given (1, 0) ||> When divide |> It should equal (Left (ArgumentException "ばーん!")) |> Verify
ArgumentExceptionが.NETなクラスなのでそりゃそうですね。
どうしよう…とtwitterに投げてみたところ。
@pocketberserker 何をテストしたいかにもよると思うけど、型があってるかどうかとメッセージがあってるかどうかを調べればいいんでね?
2012-05-03 00:53:26 via Tween to @pocketberserker
It should have (leftValue<HogeException> "ばーん")とでもして、leftValue<'x> expectedMessage xなstring -> 'x -> boolを用意するとかそんな
なるほどなるほど、早速試してみましょう。
ということで、試行錯誤の後にこんなコードを書いてみました。
open NUnit.Framework let message : (obj -> string) = function | :? string as x -> x | :? exn as x -> x.Message | x -> x.ToString() let letfValue<'x> (expectedMessage:string) (either:Either<'x,_>) = printMethod expectedMessage match either with | Left x -> Assert.AreEqual(expectedMessage, (x |> message)); true | Right _ -> false
ジェネリック型を指定しつつ、メッセージが期待するものと一致するか比較。
Left x のxがException以外の型な場合を想定して、ToStringで比較できるようにもしてみた。
それでは、はりきってテスト。
[<Scenario>] let ``割る数が0であればLeftに包まれた例外が返る`` () = Given (1, 0) ||> When divide |> It should have (letfValue<ArgumentException> "えらーしか!") |> Verify
というわけで、Eitherを使ったときの例外の型とメッセージのテストをやってみました。