OUnit で OCaml コードの単体テストを書く

OUnit という単体テストフレームワークを使ってみます。XXXUnit と「大体あってる」二項関係をもつらしいです。

open OUnit
let _ = run_test_tt_main ("test" >:: fun () -> assert_bool "should succeed" true)

assert_bool は (@?) としても定義されています。

open OUnit
let _ = run_test_tt_main ("test" >:: fun () -> "should succeed" @? true)

比較します。

open OUnit
let _ = run_test_tt_main ("test" >:: fun () -> assert_equal 0 0)

まあ普通ですね。"let (@=) = assert_equal" とかしておくといいかもしれないし、ダメかもしれない。
失敗させてみましょう。

open OUnit
let _ = run_test_tt_main ("test" >:: fun () -> assert_equal 0 1)

じっこーします。

F
==============================================================================
Failure: test

OUnit: not equal

                                                                                                                                                          • -

Ran: 1 tests in: 0.00 seconds.
FAILED: Cases: 1 Tried: 1 Errors: 0 Failures: 1 Skip:0 Todo:0

失敗してしまいました。悲しいですね…しかし何がどう間違ってたのか、値が出ないのは不親切ですね。悲しいです…
generic な print 関数がない言語なので当然でした。悲しみを乗り越えたら printer オプション引数で出力のために利用する関数を教えてあげます。

open Unit
let _ = run_test_tt_main ("test" >:: fun () -> assert_equal ~printer:string_of_int 0 1)

えいやー

F
==============================================================================
Failure: hoge

OUnit: expected: 0 but got: 1

                                                                                                                                                          • -

Ran: 1 tests in: 0.00 seconds.
FAILED: Cases: 1 Tried: 1 Errors: 0 Failures: 1 Skip:0 Todo:0

やりましたー。
値だけでてくれてもなんのこっちゃなので msg オプション引数で表示しましょう。

open Unit
let _ = run_test_tt_main ("test" >:: fun () -> assert_equal ~msg:"hogehoge" ~printer:string_of_int 0 1)

F
==============================================================================
Failure: hoge

OUnit: hogehoge
expected: 0 but got: 1

                                                                                                                                                          • -

Ran: 1 tests in: 0.00 seconds.
FAILED: Cases: 1 Tried: 1 Errors: 0 Failures: 1 Skip:0 Todo:0

極めてどうでもいいメッセージが表示されました。

テストっていうのはまあ複数あるものです。

open OUnit

let _ = run_test_tt_main begin
  "tests" >::: [
    "test 1" >:: begin fun() ->
      assert_equal 0 0
    end;
    "test 2" >:: begin fun() ->
      assert_equal 0 0
    end
  ]
end

ヤヤ、それっぽい外見になりました!まあまじめに書くならこんな感じでしょう。
(>:::) で、テストのリストに名前をつけて、一つのテストにします。

あとはまあ set up, tear down でしょうか。

open OUnit

let g = ref 0
let inc () = g := !g + 1

let set_up () = g := 0
let tear_down () = g := 0

let assert_g expected = assert_equal ~printer:string_of_int expected !g

let test =
  let impl () =
    inc ();
    assert_g 1;
    inc ();
    assert_g 2
  in "test" >:: bracket set_up impl tear_down

let _ = run_test_tt_main ("tests" >::: [test; test])

例は適当です。そのまんま haskell の bracket です。ウルトラ微妙です…

まあそんなところです。あとは run_test_tt_main ですけど、これは勝手に引数のパースとかしてくれるえらい人で、-help で詳細が見れます。

usage: ./a.out [-verbose] [-only-test path]*
-verbose Run the test in verbose mode.
-only-test path Run only the selected test
-list-test List tests
-help Display this list of options
--help Display this list of options

オプション足したりもできるようです。まあこんなもんでしょう。おわり。