QuickCheck で性質テスト

Frege には性質テスト (propery-based test) を可能にする機能が備わっています。この機能で何ができるのかを少し見ていきましょう。

タネも仕掛けも」では、以下のような関数 f および g を使用しました。

f :: [a] -> [a]  -- we should think of any such function
f  = reverse     -- that was our pick

g :: Int -> String
g x = show x ++ show x ++ show x

そして記事の結論として、

map g (f [1, 2, 3])

および

f (map g [1, 2, 3])

のいずれを選んでも同じ結果になるということがわかりました。

言い換えれば、テスト可能な性質 (個人的には 不変量 という用語のほうが好きですが) を発見したというわけです。

JUnit 界隈の人向け

JUnit のバックグラウンドを持つ人がここまで読んできた場合、「テストケースはどこにあるのか?」が気になっているに違いありません。答えはこうです。「気にしなくてよい。ドメイン内からツールが勝手に、しかもランダムに抽出する」

私が最初この考え方に出会った時、まったく馬鹿げたことのように聞こえました。しかし、実際のところこのやり方には利点があります。また不安がる必要もなく、例えばコーナーケースをカバーするために、ハードコードしたひとつひとつのテストケースを実行することも依然として可能です。

以下に示したのが、Frege で QuickCheck を用いて関数の性質を定義するやり方です。

import frege.test.QuickCheck

commutativity xs = property ( map g (f xs) == f (map g xs) )

このように性質を定義するだけで、すぐにその性質が成り立つかどうか検査することができます。

quickCheck commutativity

結果は次のようになります。

+++ OK, passed 100 tests.

これはまさに驚くべきことです! QuickCheck はどのようにして 100 個も生成すべきテストデータを知りえたのでしょうか? 引数の型すら与えてはいないのに! ここで登場するのが Frege の賢い型推論機構です。

commutativity に対して推論された型は、REPL を使えば確認できます。

:t commutativity
[Int] -> Bool

それでは、Frege の型推論エンジンが考えたに違いない内容を、順を追って見てみましょう。

関数 g は整数型の引数をひとつ取り、また map g の引数は g の引数のリストでなくてはならないため、xs としてあり得るのは整数値のリストのみです。さらに f は任意の型のリストに対して作用することができるため、追加で制約がかかることはありません。

Frege はコンパイル時に、より高度な推論を合理的に辿ってこれと同じ結論に至ります。

xs の型が [Int] であるとわかっているため、quickCheck はリスト型に任意のリスト値を (任意のサイズで) 生成させることができ、そして同様にリスト型は整数型に任意の整数値を生成させます。リストと整数はいずれも Arbitrary 型クラス のインスタンスであるため、この生成過程は型安全です。Arbitrary 型クラスとは、その型の任意の値を生成する方法が与えられている型クラスです。

関数 property についてまだ話していませんでした。これは何をするための関数でしょう?

そうですね、この関数なしで上のテストを走らせた場合も完全に正しく動作するのですが、ブール式が Property 型にラップされた値になります。Property 型にラップする利点のひとつとして、「prop_ で始まらなければならない」などといったような慣例に頼らなくとも、QuickCheck ツールが Property 型を持つすべての関数を走査しまとめて実行することができる点が挙げられます。

その他の例

すでに述べたように、ここでは型の制約を満たす限り任意の関数 fg を選ぶことができます。いくつか試してみて、全力で不変量が満たされないようにしてみましょう。本気で! 学びがあることでしょう。

ヒント: 無限リストや副作用を持つような関数 g を用いた場合、イコールの判定で問題が発生するでしょう。その場合、以下に挙げた論文を読むと助けになるかもしれません。

ここでこれ以上 QuickCheck について語ることはありません。先々の記事でまた触れることになるでしょう。

参考文献

Koen Claessen, John Hughes

QuickCheck: A Lightweight Tool for Random Testing of Haskell Programs http://www.eecs.northwestern.edu/~robby/courses/395-495-2009-fall/quick.pdf

The tool

https://github.com/Frege/frege/wiki/Getting-Started#quickcheck

results matching ""

    No results matching ""