Haskell Language
Schneller Check
Suche…
Eigenschaft deklarieren
Im einfachsten Fall ist eine Eigenschaft eine Funktion, die einen Bool
zurückgibt.
prop_reverseDoesNotChangeLength xs = length (reverse xs) == length xs
Eine Eigenschaft deklariert eine übergeordnete Invariante eines Programms. Der QuickCheck-Testläufer bewertet die Funktion mit 100 zufälligen Eingaben und prüft, ob das Ergebnis immer True
.
In der Regel haben Funktionen, die Eigenschaften sind, Namen, die mit prop_
.
Überprüfung einer einzelnen Eigenschaft
Die Funktion quickCheck
testet eine Eigenschaft an 100 zufälligen Eingaben.
ghci> quickCheck prop_reverseDoesNotChangeLength
+++ OK, passed 100 tests.
Wenn eine Eigenschaft bei einigen Eingaben fehlschlägt, gibt quickCheck
ein Gegenbeispiel aus.
prop_reverseIsAlwaysEmpty xs = reverse xs == [] -- plainly not true for all xs
ghci> quickCheck prop_reverseIsAlwaysEmpty
*** Failed! Falsifiable (after 2 tests):
[()]
Alle Eigenschaften in einer Datei prüfen
quickCheckAll
ist ein Template-Haskell-Helfer, der alle Definitionen in der aktuellen Datei findet, deren Name mit prop_
beginnt und diese testet.
{-# 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
Beachten Sie, dass die return []
-Zeile erforderlich ist. Definitionen oberhalb dieser Linie werden für Template Haskell sichtbar.
$ runhaskell QuickCheckAllExample.hs
=== prop_sortIdempotent from tree.hs:7 ===
+++ OK, passed 100 tests.
Daten zufällig für benutzerdefinierte Typen generieren
Die Arbitrary
Klasse ist für Typen, die von QuickCheck zufällig generiert werden können.
Die minimale Implementierung von Arbitrary
ist die arbitrary
Methode, die in der Gen
Monade ausgeführt wird, um einen zufälligen Wert zu erzeugen.
Hier ist eine Instanz von Arbitrary
für den folgenden Datentyp von nicht leeren Listen.
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
]
Verwenden von implication (==>) zum Prüfen von Eigenschaften mit Vorbedingungen
prop_evenNumberPlusOneIsOdd :: Integer -> Property
prop_evenNumberPlusOneIsOdd x = even x ==> odd (x + 1)
Wenn Sie prüfen möchten, ob eine Eigenschaft erfüllt ist, wenn eine Vorbedingung erfüllt ist, können Sie den Operator ==>
. Wenn es sehr unwahrscheinlich ist, dass willkürliche Eingaben der Vorbedingung entsprechen, kann QuickCheck frühzeitig aufgeben.
prop_overlySpecific x y = x == 0 ==> x * y == 0
ghci> quickCheck prop_overlySpecific
*** Gave up! Passed only 31 tests.
Begrenzung der Größe der Testdaten
Es ist schwierig, Funktionen mit geringer asymptotischer Komplexität mithilfe von Quickcheck zu testen, da die zufälligen Eingaben normalerweise nicht an die Größe gebunden sind. Durch Hinzufügen einer Obergrenze für die Größe der Eingabe können wir diese teuren Funktionen noch testen.
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
Durch die Verwendung von quickCheckWith
mit einer modifizierten Version von stdArgs
wir die Größe der Eingaben auf höchstens 10 begrenzen. stdArgs
wir Listen erstellen, bedeutet dies, dass wir Listen bis Größe 10 generieren. Unsere Permutationsfunktion funktioniert nicht Es dauert zu lange, um für diese kurzen Listen zu rennen, aber wir können immer noch recht sicher sein, dass unsere Definition richtig ist.