SMLUnitをSML#0.90からサルベージ
年明けて初めの記事ですね。今年もよろしくお願いします。
早速ですがSML#プロジェクトで開発されたユニットテストフレームワークであるSMLUnitをgithubにインポートしました(https://github.com/smlsharp/SMLUnit)。
昔はSML#のソースコードと一緒に配布されていたようですが、現状最新版の SML#1.2.0 にはSMLUnitのコードは含まれていない*1ため、手軽に使用することが出来なくなっていました。今回サルベージしたのでgit cloneしてすぐ使えます :)
SMLUnitはSML#プロジェクトの一環で開発されたユニットテスト用ライブラリですがSML'97の処理系なら問題無く動かせます。
sml-extに含まれる同名のライブラリを紹介しましたが、これとは違うライブラリです。まぁほとんど同じように使えます…。(というか似すぎてますねこれ。どっちかがフォークしたんでしょうね。)
SMLUnitの方があらかじめ定義されてるアサート用比較関数がかなり多いので便利そうです。*2
インストール
SML#用のmakefileを書いたのでmakeすればよいです:) 残念ながらreplで使うにはファイルを地道にuseする必要があります。*3
あとは必要に応じて、SML#ライブラリを置いておく場所にsmlunitlib.smiとsrc/main/*.smiをコピー。
SML/NJとMLtonに関してはreadme.mdを見てください。手順はsml-extの時と全く同じです。
使用例
sml-ext/UnitTestsの紹介に使用した例をSMLUnit用に書き直してみました。
(* test.smi *) _require "basis.smi" _require "smlunitlib.smi" (* test.sml *) structure Test = struct open SMLUnit open Assert val $ = Test.TestLabel val & = Test.TestList val % = Test.TestCase val ? = assertTrue val assertEqualEq = assertEqualInt fun test () = $ ("Test" (* 全体に名前付ける *) , &[ % (fn()=> assertEqualEq 0 0) (* 0==0 *) , % (fn()=> assertEqualEq 0 1) (* 0==1 *) , % (fn()=> assertEqualEq 0 2) , % (fn()=> assertEqualInt 1 0) (* ケースに名前を付ける *) , $ ("1==1", % (fn()=> assertEqualInt 1 1)) , % (fn()=> assertEqualInt 1 2) , % (fn()=> assertEqualReal 2.0 0.0) (* 特殊化された比較アサート *) , % (fn()=> assertEqualReal 2.1 2.1) , % (fn()=> assertEqualReal 2.2 2.19999) , % (fn()=> assertEqualReal 3.14 3.14159) , % (fn()=> ? (6 = foldr op+ 0 [1,2,3])) , $ ("sum[1..10]=55", (* ちょっと実際的?なケース *) % (fn()=> ? (55 = foldr op+ 0 (List.tabulate(10,fn i=> i))))) ]) end structure T = SMLUnit.TextUITestRunner; val _ = T.runTest {output=TextIO.stdOut} (Test.test()); (* 即テスト実行 *)
assertEqual系関数がアンカリー化されてるくらいでsml-ext.UnitTestsとほとんど同じですね(記号の関数は自分で定義してますけど)。
sml-ext用smiを書く(+管理する)よりUnitTestsから移行する方が楽そうです。
出力結果も載せておきます。
$ smlsharp test.sml -o test && ./test .FFF.FF.FF.F tests = 12, failures = 8, errors = 0 Failures: //Test/2: expected:<0>, actual:<1> //Test/3: expected:<0>, actual:<2> //Test/4: expected:<1>, actual:<0> //Test/6: expected:<1>, actual:<2> //Test/7: expected:<2.0>, actual:<0.0> //Test/9: expected:<2.2>, actual:<2.19999> //Test/10: expected:<3.14>, actual:<3.14159> //Test/12/sum[1..10]=55: expected:<true>, actual:<false> Errors:
はい問題無いですね。
というわけでSML#書くときもユニットテスト書きましょう。