Haskell Language
クイックチェック
サーチ…
プロパティの宣言
最も単純なプロパティは、 Bool
を返す関数です。
prop_reverseDoesNotChangeLength xs = length (reverse xs) == length xs
プロパティは、プログラムの高水準の不変量を宣言します。 QuickCheckテストランナーは、100個のランダム入力を使って関数を評価し、結果が常にTrue
であることを確認しTrue
。
慣例により、プロパティである関数の名前はprop_
でprop_
ます。
単一のプロパティをチェックする
quickCheck
関数は、100のランダム入力のプロパティをテストします。
ghci> quickCheck prop_reverseDoesNotChangeLength
+++ OK, passed 100 tests.
何らかの入力に対してプロパティが失敗した場合、 quickCheck
は反例を出力します。
prop_reverseIsAlwaysEmpty xs = reverse xs == [] -- plainly not true for all xs
ghci> quickCheck prop_reverseIsAlwaysEmpty
*** Failed! Falsifiable (after 2 tests):
[()]
ファイル内のすべてのプロパティのチェック
quickCheckAll
は、名前がprop_
で始まる現在のファイル内のすべての定義を見つけてテストするTemplate Haskellヘルパーです。
{-# LANGUAGE TemplateHaskell #-}
import Test.QuickCheck (quickCheckAll)
import Data.List (sort)
idempotent :: Eq a => (a -> a) -> a -> Bool
idempotent f x = f (f x) == f x
prop_sortIdempotent = idempotent sort
-- does not begin with prop_, will not be picked up by the test runner
sortDoesNotChangeLength xs = length (sort xs) == length xs
return []
main = $quickCheckAll
return []
行が必要であるreturn []
注意してください。これは、その行の上のテキスト定義をTemplate Haskellに見えるようにします。
$ runhaskell QuickCheckAllExample.hs
=== prop_sortIdempotent from tree.hs:7 ===
+++ OK, passed 100 tests.
カスタムタイプのデータをランダムに生成する
Arbitrary
クラスは、QuickCheckでランダムに生成できるタイプのクラスです。
Arbitrary
の最小限の実装はarbitrary
方法で、 Gen
モナドで実行され、ランダムな値を生成します。
空ではないリストの次のデータ型に対するArbitrary
インスタンスがあります。
import Test.QuickCheck.Arbitrary (Arbitrary(..))
import Test.QuickCheck.Gen (oneof)
import Control.Applicative ((<$>), (<*>))
data NonEmpty a = End a | Cons a (NonEmpty a)
instance Arbitrary a => Arbitrary (NonEmpty a) where
arbitrary = oneof [ -- randomly select one of the cases from the list
End <$> arbitrary, -- call a's instance of Arbitrary
Cons <$>
arbitrary <*> -- call a's instance of Arbitrary
arbitrary -- recursively call NonEmpty's instance of Arbitrary
]
含意(==>)を使用して前提条件でプロパティをチェックする
prop_evenNumberPlusOneIsOdd :: Integer -> Property
prop_evenNumberPlusOneIsOdd x = even x ==> odd (x + 1)
前提条件が成立していることを条件にプロパティが保持されていることを確認したい場合は、 ==>
演算子を使用できます。任意の入力が前提条件と一致する可能性が非常に低い場合、QuickCheckは早期に諦める可能性があることに注意してください。
prop_overlySpecific x y = x == 0 ==> x * y == 0
ghci> quickCheck prop_overlySpecific
*** Gave up! Passed only 31 tests.
テストデータのサイズを制限する
ランダム入力が通常はサイズ制限されていないため、quickcheckを使用すると、複雑な漸近的な複雑さで関数をテストすることは困難です。入力の大きさに上限を加えることによって、これらの高価な関数をテストすることができます。
import Data.List(permutations)
import Test.QuickCheck
longRunningFunction :: [a] -> Int
longRunningFunction xs = length (permutations xs)
factorial :: Integral a => a -> a
factorial n = product [1..n]
prop_numberOfPermutations xs =
longRunningFunction xs == factorial (length xs)
ghci> quickCheckWith (stdArgs { maxSize = 10}) prop_numberOfPermutations
quickCheckWith
をquickCheckWith
の修正版でstdArgs
することで、入力のサイズを10以下に制限することができます。この場合、リストを生成するときに、これはサイズ10までのリストを生成することを意味します。これらの短いリストのために実行するには時間がかかりますが、私たちはまだ私たちの定義が正しいことを合理的に確信することができます。