#:g1: cl-quickcheckの紹介

Posted 2014-10-13 00:00:00 GMT

(LISP Library 365参加エントリ)

 LISP Library 365 の286日目です。

cl-quickcheckとはなにか

 cl-quickcheckは、Darius Bacon氏作の

パッケージ情報

パッケージ名cl-quickcheck
Quicklisp
CLiKiCLiki: cl-quickcheck
Quickdocscl-quickcheck | Quickdocs
CL Test Grid: ビルド状況cl-quickcheck | CL Test Grid

インストール方法

(ql:quickload :cl-quickcheck)

試してみる

 どんな関数があるかは、Quickdocsで確認できます。

 QuickCheckは元々はHaskellの為のツールだったようですが、他の言語にも類似のものが作られていて、cl-quickcheckもその一つのようです。

QuickCheckは仕様を与えることによりテストするデータを自動生成してテストを実行します。
標準で用意されているジェネレータは下記の通りです。

(funcall an-index)
;=>  11

(funcall an-integer) ;=> -11

(funcall a-real) ;=> 10.608702

(a-char) ;=> #\E

(funcall a-boolean) ;=> T (a-symbol) ;=> |¢TK9:6~¢?9| ; NIL (a-string) ;=> "©ìÛ6"

(funcall (a-list an-index)) ;=> (16 12 3 11 9 15 19 7 4 19 13 18 8)

(funcall (a-tuple an-index a-boolean)) ;=> (2 NIL)

(funcall (a-member an-integer (a-list an-index) a-boolean)) ;=> 2

こんな感じにランダムなデータを作ることが可能です。

 これらのジェネレータで生成するデータをテストを指定してfor-allの中で回します。
テストには、test、is、isnt、is=、isnt=が使えます。
testは式が真かどうかをテスト、is=は、2つの式をequalを使ってチェック、isは、is=の述語を指定する版(removeに対するremove-if的なもの)です。

(quickcheck 
  (for-all ((a (a-list an-integer))
            (b (a-list an-integer)))
    ;; 
    (cl-quickcheck:is= `(,@a ,@b)
                       (append a b))))
;>>  Starting tests with seed #S(RANDOM-STATE :STATE #.(MAKE-ARRAY 627 :ELEMENT-TYPE
;>>                                                                '(UNSIGNED-BYTE
;>>                                                                  32)
;>>                                                                :INITIAL-CONTENTS
;>>                                                                ...))
;>>  ....................................................................................................
;>>  1 test submitted; all passed.
;=>  T

(quickcheck (for-all ((n a-real)) (is= (= 0 n) (zerop n)))) ;>> Starting tests with seed #S(RANDOM-STATE :STATE #.(MAKE-ARRAY 627 :ELEMENT-TYPE ;>> '(UNSIGNED-BYTE ;>> 32) ;>> :INITIAL-CONTENTS ;>> ...)) ;>> .................................................................................................... ;>> 1 test submitted; all passed. ;=> T

(quickcheck (for-all ((n a-real)) (is= (= 0 n) (zerop n))))

(quickcheck (for-all ((n a-real)) (is eq (= 0 n) (zerop n))))

;;; zerop test (quickcheck (for-all ((x (a-member an-integer (a-list an-index) a-boolean))) (only-if (numberp x) (is eq (= 0 x) (zerop x))) (only-if (not (numberp x)) (should-signal 'CL:ERROR (zerop x)))))

 それぞれのテストで囲みたい式がある場合はwrap-eachという構文が使えます。

(import 'cl-quickcheck::wrappee)

(quickcheck (for-all ((x a-real)) (wrap-each (progn (format t "~S == ~S~%" `(= 0 ,x) `(zerop ,x)) WRAPPEE) (is= (= 0 x) (zerop x)))))

WRAPPEEというシンボルの所が置き換えられますが、WRAPPEEは、cl-quickcheck::wrappeeでないといけません。しかしエクスポートされていないので注意(割とありがち)。

 他便利ジェネレータが用意されています。

(for-all ((k k-generator))
  (is= k k))

(for-all (k) (is= k k))

;;; (for-all (n m) (is= (+ n m) (+ m n)))

(for-all ((n n-generator) (m m-generator)) (is= (+ n m) (+ m n)))

(for-all ((n an-integer) (m an-integer)) (is= (* n m) (* m n)))

まとめ

 今回は、cl-quickcheckを紹介してみました。
cl-quickcheck網羅的にテストしたい場合には便利ですね。
ちなみにFiveAMにもQuickCheck風の機能があります。

comments powered by Disqus