Haskell Language
Szybkie sprawdzenie
Szukaj…
Deklarowanie właściwości
Najprościej mówiąc, właściwość jest funkcją, która zwraca Bool
.
prop_reverseDoesNotChangeLength xs = length (reverse xs) == length xs
Właściwość deklaruje niezmiennik wysokiego poziomu programu. Tester QuickCheck oceni funkcję za pomocą 100 losowych danych wejściowych i sprawdzi, czy wynik jest zawsze True
.
Zgodnie z konwencją funkcje będące właściwościami mają nazwy rozpoczynające się od prop_
.
Sprawdzanie pojedynczej właściwości
Funkcja quickCheck
sprawdzania testuje właściwość na 100 losowych wejściach.
ghci> quickCheck prop_reverseDoesNotChangeLength
+++ OK, passed 100 tests.
Jeśli właściwość nie powiedzie się dla niektórych danych wejściowych, quickCheck
wydrukuje kontrprzykład.
prop_reverseIsAlwaysEmpty xs = reverse xs == [] -- plainly not true for all xs
ghci> quickCheck prop_reverseIsAlwaysEmpty
*** Failed! Falsifiable (after 2 tests):
[()]
Sprawdzanie wszystkich właściwości w pliku
quickCheckAll
to pomocnik szablonów Haskell, który znajduje wszystkie definicje w bieżącym pliku, którego nazwa zaczyna się od prop_
i testuje je.
{-# 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
Zauważ, że wymagany jest wiersz return []
. To sprawia, że definicje tekstowo powyżej tej linii są widoczne dla Template Haskell.
$ runhaskell QuickCheckAllExample.hs
=== prop_sortIdempotent from tree.hs:7 ===
+++ OK, passed 100 tests.
Losowe generowanie danych dla niestandardowych typów
Klasa Arbitrary
jest przeznaczona dla typów, które mogą być generowane losowo przez QuickCheck.
Minimalna implementacja Arbitrary
to arbitrary
metoda, która działa w monadzie Gen
celu wygenerowania losowej wartości.
Oto przykład Arbitrary
dla następującego typu danych niepustych list.
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
]
Korzystanie z implikacji (==>) w celu sprawdzenia właściwości z warunkami wstępnymi
prop_evenNumberPlusOneIsOdd :: Integer -> Property
prop_evenNumberPlusOneIsOdd x = even x ==> odd (x + 1)
Jeśli chcesz sprawdzić, czy właściwość jest przechowywana, biorąc pod uwagę, że warunek ten jest spełniony, możesz użyć operatora ==>
. Zauważ, że jeśli bardzo mało prawdopodobne jest, aby arbitralne dane wejściowe pasowały do warunku wstępnego, QuickCheck może zrezygnować wcześnie.
prop_overlySpecific x y = x == 0 ==> x * y == 0
ghci> quickCheck prop_overlySpecific
*** Gave up! Passed only 31 tests.
Ograniczanie wielkości danych testowych
Testowanie funkcji o niskiej złożoności asymptotycznej może być trudne przy użyciu szybkiej kontroli, ponieważ losowe dane wejściowe zwykle nie są ograniczone rozmiarem. Dodając górną granicę wielkości wejścia, możemy nadal testować te drogie funkcje.
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
Używając quickCheckWith
ze zmodyfikowaną wersją stdArgs
, możemy ograniczyć rozmiar danych wejściowych do maksymalnie 10. W tym przypadku, ponieważ generujemy listy, oznacza to, że generujemy listy do rozmiaru 10. Nasza funkcja permutacji nie zbyt długo pracujemy nad tymi krótkimi listami, ale nadal możemy być dość pewni, że nasza definicja jest poprawna.