a wandering wolf

Does a wandering wolf dreams of a wondering, sometimes programming sheep?

このエントリーをはてなブックマークに追加

Persimmon でテストを書く

さて、昨日の記事 でご紹介した Funcy は、そのテストにちょっと変わったフレームワークを使っています。

persimmon-projects/Persimmon - Github

こちらの Persimmon(通称「柿」)、以前記事にした F# 製テスティング・フレームワークです。

特徴

Persimmon の特徴は、F# のコンピュテーション式という構文拡張機能を使用して DSL を提供している点です。

公式ドキュメントにあるサンプルコードを眺めてみます。例えば4を2で割った余りが0になるかどうかを確認するなら、以下のように書きます。

open Persimmon

let ``some variable name`` = test "first test example" {
    do! assertEquals 0 (4 % 2)
}

test "テスト名" { (* 処理 *) } という具合に書きます。

例外を扱うときは trap { (* 例外を送出するかもしれない処理 *) } と書きます:

exception MyException

let ``exception test`` = test "exn test" {
  let f () =
    raise MyException
    42
  let! e = trap { it (f ()) }
  do! assertEquals "" e.Message
  do! assertEquals typeof<MyException> (e.GetType())
  do! assertEquals "" (e.StackTrace.Substring(0, 5))
}

この他、詳しいことは公式ページや中の人が教えてくれるはずです。

思いとか

柿でテストを書いてて、「こうだったらいいなぁ」という思いも出てきました。

let t1 = test "切ないテスト" {
  let ms = new System.IO.MemoryStream([| 0x33uy; 0x04uy |])
  (* ms: MemoryStream についての何らかの操作やテスト *)
  do! assertEquals () <| ms.Dispose()
}

これ、何してるかお分かりになりますか?

MemoryStream 型の引数を使ったので、それを適切に破棄しようとしています。

本当は、こう書きたいわけです:

let t2 = test "望ましいテスト" {
  use ms = new System.IO.MemoryStream([| 0x33uy; 0x04uy |])
  (* ms: MemoryStream についての何らかの操作やテスト *)
}

このテストの間にあるギャップは2つあります:

  1. ビルダークラスに Using メソッドが定義されていないため、use 式が使えない(リソースの破棄に気をつけなければならない)
  2. ビルダークラスに Zero メソッドが定義されていないため、unit を返すような処理の扱いに悩む(特に最後が unit を返す操作で終わる場合)

いま Persimmon で提供されている機能との整合性との兼ね合いですが、この辺りが上手くできるようになると、もっとテストが書きやすくなりそうです。(僕が知らないだけかもしれないから、良いやり方を教えてもらいたい!)

最後に

ケンシロウ、Persimmon はいいぞ!

追記(2015/7/7)

@pocketberserker さんがアンサーエントリを書いてくれました!

「Persimmonでテストを書く」での疑問点に回答してみる - pocketberserkerの爆走

Using が使えるようになるの、嬉しいですね!