はじめに
test.checkはユニットテストのためのライブラリです。(以前はsimple-checkという名前でした)
ランダムに生成した値をテスト対象に入力して、簡単にその挙動をテストすることができます。
開発者が考えた具体的な入力値でテストを行うだけではどこかに抜け漏れが生じる可能性がありますが、
test.checkでは毎回違う値でテストを実行するので、このような抜け漏れを見つけやすく、プログラムの信頼性をより高めることができます。
また、test.checkはHaskellのQuickCheckを元にClojureへ向けに移植されたライブラリで、Clojure以外にもScala, Ruby, Pythonなど多くの言語に移植されているようです。
さっそく触ってみる
test.checkの導入
test.checkを使うにはprojec.cljの:dependencies
にtest.checkを追加します
...
[org.clojure/test.check "0.9.0"]
...
ランダムテスト
ここでは以下のproductコードに対してテストを行っていきます
(ns sample-testcheck.core)
(defn hello
[x]
(str "Hello, " x))
テストコードは以下のように書いてみました
(ns sample-testcheck.core-test
(:require [clojure.test :refer :all]
[sample-testcheck.core :refer :all]
[clojure.test.check.generators :as gen]
[clojure.test.check.properties :as prop]
[clojure.test.check.clojure-test :refer [defspec]]))
(defspec ; clojure.test integration
hello-test ; test名
100 ; 試行回数
(prop/for-all
[x gen/string] ; ランダム値の生成
(= (hello x) (str "Hello, " x)))) ; check
(本来ならプロダクトコードと同等のIn/Outになるロジックで確認するところですが、簡単のためproductコードの中身でそのもので確認しています)
実際にテストを走らせてみると以下のように出力されます。
$ lein test
lein test sample-testcheck.core-test
{:result true, :num-tests 100, :seed 1449985781679, :test-var "hello-test"}
Ran 1 tests containing 1 assertions.
0 failures, 0 errors.
これだけでランダムに生成した100パターンの入力値でテストすることができました。
defspec
defspec
マクロを使うと、test.checkで書かれたテストをclojure.testのテストケースとして取り扱ってくれます
generators
ランダム値はgeneratorsを使うことで簡単に生成することができます。
ここではstringを例にしていますが、int, list, mapなども生成可能です。
また、パターンの多くないことがわかっているケースでは、generators/element
を使って特定の要素からランダム値を生成することも可能です。
generatorsはいろいろなランダム値を生成できるので、以下のcheat sheetに目を通しておくと良いかもしれません。
https://github.com/clojure/test.check/blob/master/doc/cheatsheet.md
おわりに
test.checkを使うことで簡単にランダムテストを行うことができることがわかりました。
また、今回はClojureのテストを行いましたが、もちろんClojureScriptにも使用できます。
generatorsの種類は結構豊富ですが、他の点はとても単純なので非常に使いやすいライブラリかと思います。
より多くの入力値で確認しておきたいようなケース(parserや暗号化/複号化など)ではとても役立つかもしれませんね。
参考
test.check
test.check APIドキュメント
はじめての test.check 0.7.0 - tnoda-clojure
QuickCheck - Wikipedia, the free encyclopedia