Haskell Language
Vérification rapide
Recherche…
Déclarer une propriété
Dans sa forme la plus simple, une propriété est une fonction qui renvoie un Bool
.
prop_reverseDoesNotChangeLength xs = length (reverse xs) == length xs
Une propriété déclare un invariant de haut niveau d'un programme. Le programme de test QuickCheck évalue la fonction avec 100 entrées aléatoires et vérifie que le résultat est toujours True
.
Par convention, les fonctions qui sont des propriétés ont des noms qui commencent par prop_
.
Vérification d'une seule propriété
La fonction quickCheck
teste une propriété sur 100 entrées aléatoires.
ghci> quickCheck prop_reverseDoesNotChangeLength
+++ OK, passed 100 tests.
Si une propriété échoue pour une entrée, quickCheck
un contre-exemple.
prop_reverseIsAlwaysEmpty xs = reverse xs == [] -- plainly not true for all xs
ghci> quickCheck prop_reverseIsAlwaysEmpty
*** Failed! Falsifiable (after 2 tests):
[()]
Vérification de toutes les propriétés dans un fichier
quickCheckAll
est un assistant Template Haskell qui trouve toutes les définitions du fichier en cours dont le nom commence par prop_
et les teste.
{-# 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
Notez que la ligne de return []
est requise. Il rend les définitions textuellement au-dessus de cette ligne visibles pour Template Haskell.
$ runhaskell QuickCheckAllExample.hs
=== prop_sortIdempotent from tree.hs:7 ===
+++ OK, passed 100 tests.
Génération aléatoire de données pour les types personnalisés
La classe Arbitrary
est pour les types pouvant être générés de manière aléatoire par QuickCheck.
L'implémentation minimale d' Arbitrary
est la méthode arbitrary
, qui s'exécute dans la monade Gen
pour produire une valeur aléatoire.
Voici une instance de Arbitrary
pour le type de données suivant des listes non vides.
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
]
Utilisation de l'implication (==>) pour vérifier les propriétés avec des conditions préalables
prop_evenNumberPlusOneIsOdd :: Integer -> Property
prop_evenNumberPlusOneIsOdd x = even x ==> odd (x + 1)
Si vous voulez vérifier qu'une propriété est conservée si une précondition est vérifiée, vous pouvez utiliser l'opérateur ==>
. Notez que s'il est très improbable que des entrées arbitraires correspondent à la condition préalable, QuickCheck peut abandonner tôt.
prop_overlySpecific x y = x == 0 ==> x * y == 0
ghci> quickCheck prop_overlySpecific
*** Gave up! Passed only 31 tests.
Limiter la taille des données de test
Il peut être difficile de tester des fonctions avec une complexité asymptotique médiocre en utilisant la vérification rapide car les entrées aléatoires ne sont généralement pas limitées par la taille. En ajoutant une limite supérieure à la taille de l'entrée, nous pouvons toujours tester ces fonctions coûteuses.
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
En utilisant quickCheckWith
avec une version modifiée de stdArgs
nous pouvons limiter la taille des entrées à 10 au maximum. Dans ce cas, comme nous générons des listes, cela signifie que nous générons des listes allant jusqu'à la taille 10. Notre fonction de permutations ne prendre trop de temps pour courir pour ces listes restreintes, mais nous pouvons toujours être raisonnablement convaincus que notre définition est correcte.