What is it, naokirin?

ちょっと気軽なOUnit:pa_ounit

OCamlを使っている皆さん。テストコードは書きますよね。
そこでOUnitの登場です。
OUnitはOCamlのユニットテスティングフレームワークです。OSSなどでも良く使われています。

…が、OUnitは結構冗長、というよりテスト自体の本質が見えにくくなることがあります。
そのため、結構シンプルなテスト以外が行われていないOSSもある気がします。(強い静的型付けなのでそれでもバグは少ないのでしょうが…)

そこで(あまりうれしくない部分もありますが)、pa_ounitというライブラリを紹介しておきます。
https://github.com/till-varoquaux/pa_ounit
(JS Core を使っている人はすでにJS Coreに付属しているpa_ounitが入っていると思います。)

「ちょっと気軽な」と書きましたがpa_ounit自体の中身はP4というドキュメント整備されてない類いのツールが使われているので、あまり中身を見るのをお勧めしたくなる代物ではありません…(まだコード自体は短いので複雑なP4を用いたライブラリよりは読めなくもない)


実際にどのようにpa_ounitを使うと書けるか、紹介しましょう。

let fizzbuzz n =
  if n = 0 then raise Invalid_arg n
  match n mod 3, n mod 5 with
  | 0, 0 -> "FizzBuzz"
  | 0, _ -> "Fizz"
  | _, 0 -> "Buzz"
  | _ -> string_of_int n

(* 単純なテスト *)
TEST "fizzbuzz 1 = 1" = fizzbuzz 1 = "1"
TEST "fizzbuzz 3 = Fizz" = fizzbuzz 3 = "Fizz"
TEST "fizzbuzz 5 = Buzz" = fizzbuzz 5 = "Buzz"
TEST "fizzbuzz 15 = FizzBuzz" = fizzbuzz 15 = "FizzBuzz"

(* 例外のテスト *)
TEST_FAILS "raise Invalid_argument if the argument is less than 1"
  RAISES (Invalid_argument _) : string =
    fizzbuzz 0
TEST_FAILS : string = fizzbuzz ~-1

(* テストをモジュールとしてまとめておく事も出来る *)
TEST_MODULE "fizzbuzz_test" = struct
  TEST = fizzbuzz 1 = "1"
  TEST = fizzbuzz 3 = "Fizz"
  TEST = fizzbuzz 5 = "Buzz"
  TEST = fizzbuzz 15 = "FizzBuzz"
end

(* ここでテスト実行 *)
let _ =
  OUnit.run_test_tt_main ( ounit_tests() )

上記fizzbuzz.mlというファイルに書き込んだとしましょう。

そのあと、

$ ocamlfind ocamlopt  -package pa_ounit -syntax camlp4o -c fizzbuzz.ml
$ ocamlfind ocamlopt -linkpkg -package pa_ounit fizzbuzz.cmx -o fizzbuzz.native
$ ./fizzbuzz.native

とすれば、テストが実行できます。もちろんテストコードとプロダクトコードを分離する事も可能です。

非常に簡潔にテストコードが記述できるpa_ounitですが、弱点としては
テスト対象の結果の値が表示されない
これにつきます。
表示する方法があるのかもしれませんが、見つけてません。(多分、ソースコードを見る限り無いと思います。)

テスティングフレームワークとしてメインで使っていくには、結構大きな弱点です。なにせ失敗時の状況が分からないですし。

とはいえ、OUnitだけでは難しいテストコードの複雑さの解消に使えるのではないでしょうか。
こういうのは使い分け、ですね。