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.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow