Funktionale Programmierung

Werbung
Quickcheck
Funktionale Programmierung
Fallstudie Quickcheck
D. Rösner
Institut für Wissens- und Sprachverarbeitung
Fakultät für Informatik
Otto-von-Guericke Universität Magdeburg
c
Sommer 2014, 20. Juni 2014, 2011
- 14 D.Rösner
D. Rösner FP 2014 . . .
1
Quickcheck
Gliederung
1
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
D. Rösner FP 2014 . . .
2
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Fallstudie: Testen von Haskell-Programmen
Programm QuickCheck
Quelle:
http://www.cs.chalmers.se/~rjmh/QuickCheck
in GHCi: import Test.QuickCheck
Grundidee: Benutzer definiert Eigenschaften, die ein
Programm erfüllen soll
diese Eigenschaften werden dann an einer grossen Zahl
automatisch generierter Testfälle überprüft
ursprünglich für Haskell entwickelt, gibt es
Implementationen von QuickCheck mittlerweile für
zahlreiche andere Sprachen (u.a. für Erlang)
Originalarbeit: [CH00]
D. Rösner FP 2014 . . .
4
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Fallstudie: QuickCheck
Beispiel:
Definition einer Eigenschaft
prop_RevRev xs = reverse(reverse xs) == xs
where types = xs::[Int]
Testen einer Eigenschaft
*Main> quickCheck prop_RevRev
+++ OK, passed 100 tests.
D. Rösner FP 2014 . . .
5
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Fallstudie: QuickCheck
Beispiel:
Definition einer Eigenschaft
prop_RevId xs = reverse xs == xs
where types = xs::[Int]
Testen und Falsifizieren einer Eigenschaft
*Main> quickCheck prop_RevId
*** Failed! Falsifiable (after 6 tests and 1 shrink):
[0,1]
*Main> quickCheck prop_RevId
*** Failed! Falsifiable (after 4 tests and 2 shrinks)
[0,1]
D. Rösner FP 2014 . . .
6
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Fallstudie: QuickCheck
Beispiel: ausführliches Ablaufprotokoll:
*Main> verboseCheck prop_RevId
Passed:
[]
Passed:
[-1]
Failed:
[0,-1]
*** Failed! Passed:
[]
Passed:
[-1]
Passed:
[0]
Failed:
[0,1]
Passed:
[]
Passed:
[1]
Passed:
[0]
Passed:
[0,0]
Falsifiable (after 3 tests and 1 shrink):
D. Rösner FP 2014 . . .
[0,1]
7
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Fallstudie: Testen von Haskell-Programmen
Programm QuickCheck erlaubt
Testen bedingter Eigenschaften
Testen quantifizierter Eigenschaften
Steuerung der Verteilung der Testfälle
Klassifizieren und Zählen von Testfällen
Sammeln von Datenwerten
D. Rösner FP 2014 . . .
9
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Testen bedingter Eigenschaften
Eigenschaften können die Form haben
<condition> ==> <property>
eine solche Eigenschaft gilt genau dann, wenn die
Eigenschaft nach dem ==> immer gilt, wenn die
<condition> gilt
beim Testen werden nur Testfälle betrachtet, welche die
<condition> erfüllen ; falls nach einer vorgegebenen
Anzahl von Versuchen (derzeit: 1000) noch keine 100
Testfälle gefunden, wird beendet und Meldung
ausgegeben wie
Arguments exhausted after 97 test.
D. Rösner FP 2014 . . .
10
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Testen bedingter Eigenschaften
Eigenschaft:
prop_Insert x xs =
ordered xs ==> collect (length xs)$
ordered (insert x xs)
where types = x::Int
Testläufe:
*Main> quickCheck prop_Insert
*** Gave up! Passed only 72 tests:
44% 0
33% 1
16% 2
5% 3
*Main> quickCheck prop_Insert
*** Gave up! Passed only 82 tests:
39% 0
32% 1
20% 2
4% 3
2% 4
D. Rösner FP 2014 . . .
11
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Testen quantifizierter Eigenschaften
Eigenschaften können die Form haben
forAll <generator> $ \<pattern> -> <property>
Beispiel:
prop_Insert2 x
= forAll orderedList $ \xs -> ordered (insert x xs)
where types = x::Int
das erste Argument des forAll ist ein
Testdatengenerator
orderedList :: (Ord a, Arbitrary a) => Gen [a]
mit solchen speziellen Generatoren lässt sich die
Verteilung der Testdaten besser kontrollieren als durch
nachträgliches Filtern
D. Rösner FP 2014 . . .
12
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Testen quantifizierter Eigenschaften
*Main> verboseCheck prop_Insert2
Passed:
-1
[]
Passed:
-1
[-1]
...
Passed:
2
[-4,4]
Passed:
-4
[-4,-4,-3,3]
...
Passed:
-2
[-15,-10,-6,-5,-3,-3,-3,0,1,10]
Passed:
-1
[-14,-4,-4,5,8]
Passed:
-10
[-15,-10,-8,-6,-5,-4,-3,0,4,15,16,16]
...
D. Rösner FP 2014 . . .
+++ OK, passed 100 tests.
13
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Klassifikation von Testfällen
Eigenschaften können die Form haben
classify <condition> <string>$ <property>
Beispiel:
prop_Insert3 x xs =
ordered xs ==>
classify (ordered (x:xs)) "at-head"$
classify (ordered (xs++[x])) "at-tail"$
ordered (insert x xs)
where types = x::Int
D. Rösner FP 2014 . . .
14
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Klassifikation von Testfällen
Verteilung der Testfälle (die mehreren Kategorien
angehören können) wird berichtet
Beispiel:
*Main> quickCheck prop_Insert3
*** Gave up! Passed only 74 tests:
41% at-head, at-tail
24% at-head
22% at-tail
D. Rösner FP 2014 . . .
15
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Klassifikation von Testfällen
Eigenschaften können die Form haben
classify <condition> <string>$ <property>
Beispiel:
prop_RemDup4’’ xs =
classify (null xs) "empty list" $
classify (length xs == 1) "singleton" $
classify (length xs == 2) "pair list" $
classify (length xs > 2) "three or more elems" $
classify ((length xs > 2) && (allDifferent xs))
"nontrivially all different" $
(noRepetitions (remDuplicates xs))
where types = xs::[Int]
D. Rösner FP 2014 . . .
16
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Klassifikation von Testfällen
Beispiel:
*Main> quickCheck prop_RemDup4’’
+++ OK, passed 100 tests:
81% three or more elems, nontrivially all different
8% three or more elems
6% singleton
4% empty list
1% pair list
*Main> quickCheck prop_RemDup4’’
*** Failed! Falsifiable (after 10 tests and 2 shrinks):
[-8,-8,-8]
getestet wurde die folgende fehlerhafte Implementierung:
-- faulty version 2: does miss three consecutives
remDuplicates [] = []
remDuplicates [x] = [x]
remDuplicates (x:y:xs) = if x == y then y:remDuplicates xs
else x : remDuplicates (y:xs)
D. Rösner FP 2014 . . .
17
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Sammeln von Datenwerten
Eigenschaften können die Form haben
collect <expression>$ <property>
Beispiel:
prop_Insert x xs =
ordered xs ==> collect (length xs)$
ordered (insert x xs)
where types = x::Int
die Verteilung der Werte des Arguments von collect
wird berichtet
D. Rösner FP 2014 . . .
18
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Sammeln von Datenwerten
Beispiel:
prop_RevRevColl xs = collect (length xs) $
reverse(reverse xs) == xs
where types = xs::[Int]
*Main> quickCheck prop_RevRevColl
+++ OK, passed 100 tests:
6% 1
5% 13
4% 5
4% 39
4% 23
4% 2
4% 12
3% 4
3% 35
3% 31
3% 3
3% 26
...
1% 27
1% 18
1% 16
1% 10
D. Rösner FP 2014 . . .
19
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Datenstrukturen
Beispiel:
data Card = Card Int String
deriving (Eq,Show)
für das Erzeugen zufälliger Instanzen:
instance Arbitrary Card where
arbitrary =
do
int
<- arbitrary -- of type Gen Int
string <- arbitrary -- of type Gen String
return (Card int string)
[Tho11], Ch. 19
D. Rösner FP 2014 . . .
21
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Datenstrukturen
Beispiel:
*Main> sample (arbitrary :: Gen Card)
Card 1 ""
Card (-1) ""
Card (-1) "?"
Card 2 "\201B\170\v"
Card (-4) "\232‘\GS\201\SO%D"
Card 16 "\233\ETX\SOHNP\\"
Card (-15) "\233n2\183U@y$}\254"
Card (-1) "\218m\RSl\a\132\180"
Card (-44) ";\138zTL"
Card 75 "4\150\178t0oK\193\EM\156Q\ESCk\ny"
Card 58 "\157xxU\ACKN\STXK\141\GS\GS\217VV\EOT"
[Tho11], Ch. 19
D. Rösner FP 2014 . . .
22
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Datenstrukturen
Beispiel:
data Info = Number Int | Email String
deriving (Eq,Show)
instance Arbitrary Info where
arbitrary =
do
boo <- arbitrary
if boo
then do
int <- arbitrary
return (Number int)
else do
string <- arbitrary
return (Email string)
[Tho11], Ch. 19
D. Rösner FP 2014 . . .
23
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Datenstrukturen
Beispiel:
*Main> sample (arbitrary :: Gen Info)
Email ""
Email "<\154"
Email "zE\190R"
Email "\tG\\\ETB\NUL"
Number (-6)
Number 9
Email "D\173;d"
Email ""
Email "m\EOTw"
Number 44
Email "BOs \180\131L\SOHy)"
*Main> sample (arbitrary :: Gen Info)
Email ""
Number 1
Email "M"
Email "<\173\DELRk8"
Email "P\EOT\150)"
Number (-16)
Email "(\243\179iR!F\189"
Number (-3)
Email "\188a"
Number 46
Number 109
D. Rösner FP 2014 . . .
24
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Datenstrukturen
Beispiel:
data List a = Empty | Cons a (List a)
deriving (Eq,Show)
instance Arbitrary a => Arbitrary (List a) where
arbitrary =
do
boo <- arbitrary
if boo
then return Empty
else do
a1 <- arbitrary
l1 <- arbitrary
return (Cons a1 l1)
[Tho11], Ch. 19
D. Rösner FP 2014 . . .
25
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Datenstrukturen
Beispiel:
*Main> sample (arbitrary :: Gen (List Int))
Empty
Cons (-1) (Cons (-1) (Cons 1 Empty))
Cons (-1) Empty
Empty
Cons 8 (Cons (-6) (Cons 2 (Cons 2 Empty)))
Empty
Empty
Cons 22 Empty
Empty
Cons (-74) Empty
Empty
*Main> sample (arbitrary :: Gen (List String))
Empty
Cons "" (Cons "" Empty)
Empty
Empty
Cons "" Empty
Cons "k\\~\ak\202\SIf" Empty
Empty
Cons "M\166\180\142\231?lmP[H=" (Cons "\128\DC3," Empty)
Empty
Empty
Empty
D. Rösner FP 2014 . . .
26
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Rekursive Datenstrukturen
Beispiel:
data Expr = Lit Integer |
Add Expr Expr |
Sub Expr Expr
deriving (Eq,Show)
instance Arbitrary Expr where
arbitrary = sized arbExpr
*Main> :t sized
sized :: (Int -> Gen a) -> Gen a
[Tho11], Ch. 19
D. Rösner FP 2014 . . .
27
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Rekursive Datenstrukturen
Implementierung 1
arbExpr 0 =
do int <- arbitrary
return (Lit int)
arbExpr n
| n>0 =
do
pick <- choose (0,2::Int)
case pick of
0 -> do
int <- arbitrary
return (Lit int)
1 -> do
left <- subExp
right <- subExp
return (Add left right)
2 -> do
left <- subExp
right <- subExp
return (Sub left right)
where
subExp = arbExpr (div n 2)
[Tho11], Ch. 19
D. Rösner FP 2014 . . .
28
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Rekursive Datenstrukturen
Implementierung 2
{- reimplementation with frequency and liftM (from Control.Monad)
arbExpr 0 = liftM Lit arbitrary
arbExpr
[(1,
(2,
(2,
n = frequency
liftM Lit arbitrary),
liftM2 Add subExp subExp),
liftM2 Sub subExp subExp)]
where
subExp = arbExpr (div n 2)
Control.Monad:
*Main> :t liftM
liftM :: Monad m => (a1 -> r) -> m a1 -> m r
*Main> :t liftM2
liftM2 :: Monad m => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r
[Tho11], Ch. 19
D. Rösner FP 2014 . . .
29
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Rekursive Datenstrukturen
*Main> sample (arbitrary :: Gen Expr)
Lit 0
Sub (Add (Lit 2) (Lit 0)) (Sub (Lit 0) (Lit (-1)))
Sub (Lit 2) (Sub (Lit (-4)) (Add (Lit (-2)) (Lit 0)))
Add (Sub (Sub (Lit 5) (Lit (-1))) (Add (Lit 2) (Lit (-2)))) (Sub (Lit (-6))
(Add (Lit 5) (Lit (-5))))
Lit 2
Add (Sub (Lit (-7)) (Sub (Sub (Lit 7) (Lit (-9))) (Lit (-3)))) (Sub (Lit (-4))
(Sub (Lit 8) (Sub (Lit 6) (Lit 7))))
Sub (Lit 3) (Add (Add (Lit 8) (Lit 0)) (Lit 10))
Lit (-10)
Lit 11
Sub (Sub (Add (Lit 8) (Sub (Add (Lit (-4)) (Lit 11)) (Sub (Lit 11) (Lit (-3)))))
(Lit (-13))) (Add (Add (Add (Add (Lit 14) (Lit (-7))) (Sub (Lit (-7)) (Lit (-14))))
(Add (Sub (Lit (-2)) (Lit (-11))) (Add (Lit (-11)) (Lit (-9))))) (Lit (-10)))
Add (Sub (Add (Sub (Sub (Lit 3) (Lit (-15))) (Add (Lit (-11)) (Lit 16))) (Lit (-7)))
(Sub (Add (Sub (Lit (-10)) (Lit (-6))) (Sub (Lit (-2)) (Lit 20))) (Lit (-11))))
(Sub (Lit 10) (Lit (-1)))
[Tho11], Ch. 19
D. Rösner FP 2014 . . .
30
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Eigenschaften von Funktionen
Beispiel:
prop_map_1 f g xs =
map (f::Int->Int) (map (g::Int->Int) xs) == map (f.g) xs
Beispiel:
prop_map_2 f g xs =
map (f::Int->Int) (map (g::Int->Int) xs) == map (g.f) xs
Welche ist zutreffend?
[Tho11], Ch. 19
D. Rösner FP 2014 . . .
32
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Eigenschaften von Funktionen
Beispiel:
*Main> quickCheck prop_map_1
+++ OK, passed 100 tests.
Beispiel:
*Main> quickCheck prop_map_2
*** Failed! Falsifiable (after 3 tests and 2 shrinks):
(1|->0) ,(-1|->1) ,(0|->1) ,(-2|->1) ,(-3|->0) ,(-1|->1) ,(11|->-1) ,(22|->-1)
,(15|->1) ,(-28|->-1) ,(-202|->-1)
(0|->-1) ,(-1|->1) ,(-2|->1) ,(-3|->1) ,(5|->0) ,(-9|->0) ,(-3|->1) ,(-10|->1)
,(36|->0) ,(20|->0) ,(-197|->-1)
[0]
Interpretation:
zunächst die Abbildungsvorschrift von f, dann die von g
auf Testliste [0] gilt Behauptung nicht
f 0 = 1, g 1 = 0 (Default); aber:
g 0 = -1, f (-1) = 1
[Tho11], Ch. 19
D. Rösner FP 2014 . . .
33
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Eigenschaften von Funktionen
*Main> verboseCheck prop_map_2
Passed:
(0|->-1) ,(1|->-1) ,(-2|->0) ,(3|->0) ,(8|->1) ,(2|->0) ,(-14|->0) ,(-15|->0)
,(39|->0) ,(-10|->1) ,(170|->1)
(1|->-1) ,(-1|->0) ,(1|->-1) ,(-1|->0) ,(5|->1) ,(-12|->0) ,(2|->-1) ,(-19|->0)
,(-32|->-1) ,(-125|->0) ,(22|->1)
[]
Passed:
...
Failed:
(0|->0) ,(0|->0) ,(0|->0) ,(1|->1) ,(-7|->0) ,(8|->1) ,(-1|->0) ,(12|->-1) ,(-28|->2)
,(-114|->-1) ,(-105|->2)
(0|->-2) ,(0|->-2) ,(-1|->-2) ,(2|->-1) ,(7|->-1) ,(5|->2) ,(9|->-2) ,(8|->-1)
,(-14|->2) ,(6|->-2) ,(-197|->2)
[0,-1,-1,1]
*** Failed! Passed:
...
Failed:
...
Falsifiable (after 5 tests and 2 shrinks):
(0|->0) ,(0|->0) ,(0|->0) ,(1|->1) ,(-7|->0) ,(8|->1) ,(-1|->0) ,(12|->-1) ,(-28|->2)
,(-114|->-1) ,(-105|->2)
(0|->-2) ,(0|->-2) ,(-1|->-2) ,(2|->-1) ,(7|->-1) ,(5|->2) ,(9|->-2) ,(8|->-1) ,(-14|->2)
,(6|->-2) ,(-197|->2)
[1]
D. Rösner FP 2014 . . .
34
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Fallstudie: QuickCheck
zentral sind Generatoren für Testdaten
Typ:
newtype Gen a
= Gen (Int -> StdGen -> a)
Typ Gen ist Funktor und Monade
D. Rösner FP 2014 . . .
36
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Fallstudie: QuickCheck
Definition als Funktor:
instance Functor Gen where
fmap f m = m >>= return . f
Definition als Monade:
instance Monad Gen where
return a
= Gen (\n r -> a)
Gen m >>= k =
Gen (\n r0 -> let (r1,r2) = split r0
Gen m’ = k (m n r1)
in m’ n r2)
D. Rösner FP 2014 . . .
37
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Fallstudie: QuickCheck
zentrale Funktion:
choose :: Random a => (a, a) -> Gen a
choose bounds = (fst . randomR bounds) ‘fmap‘ rand
rand :: Gen StdGen
rand = Gen (\n r -> r)
D. Rösner FP 2014 . . .
38
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Fallstudie: QuickCheck
Kombinatoren für Generatoren:
Auswahl aus einer Liste von Generatoren
oneof :: [Gen a] -> Gen a
oneof gens = elements gens >>= id
Beispiel:
oneof [return True, return False]
D. Rösner FP 2014 . . .
39
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Fallstudie: QuickCheck
Kombinatoren für Generatoren:
Beeinflussung der Verteilung:
frequency :: [(Int, Gen a)] -> Gen a
frequency xs = choose (1, tot) >>= (‘pick‘ xs)
where
tot = sum (map fst xs)
pick n ((k,x):xs)
| n <= k
= x
| otherwise = pick (n-k) xs
Beispiel:
frequency [(2,return True), (1,return False)]
D. Rösner FP 2014 . . .
40
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Fallstudie: QuickCheck
vordefinierte Generatoren für zahlreiche Typen
Überladen der Methoden der Klasse Arbitrary
class Arbitrary a where
arbitrary
:: Gen a
coarbitrary :: a -> Gen b -> Gen b
arbitrary . . . für zufällige Werte des Typs a
coarbitrary . . . für zufällige Funktionen des Typs a-> b
D. Rösner FP 2014 . . .
41
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Fallstudie: QuickCheck
vordefinierte Generatoren für zahlreiche Typen
Beispiel: Typ Arbitrary Int
instance Arbitrary Int where
arbitrary
= sized $ \n -> choose (-n,n)
coarbitrary n
= variant (if n >= 0 then 2*n else 2*(-n) + 1)
D. Rösner FP 2014 . . .
42
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Fallstudie: QuickCheck
vordefinierte Generatoren für zahlreiche Typen
Beispiel: Typ Arbitrary (a, b):
instance (Arbitrary a, Arbitrary b) => Arbitrary (a, b)
where
arbitrary
= liftM2 (,) arbitrary arbitrary
coarbitrary (a, b) = coarbitrary a . coarbitrary b
verwendet wird hier die monadische Funktion liftM2
liftM2 :: (Monad m) => (a -> b -> c) -> (m a -> m b -> m c)
liftM2 f = \a b -> do { a’ <- a; b’ <- b; return (f a’ b’) }
D. Rösner FP 2014 . . .
43
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Implementation
Kombinatoren, die Generatoren produzieren:
elements . . . erzeuge zu einem aus einer Liste zufällig
ausgewählten Element einen Generator
elements :: [a] -> Gen a
elements xs
= (xs !!) ‘fmap‘ choose (0, length xs - 1)
D. Rösner FP 2014 . . .
45
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Implementation
Beispiele:
*Main> sample (elements [’a’..’f’])
’c’
’d’
’f’
’d’
’f’
’d’
’d’
’a’
’e’
’e’
’b’
D. Rösner FP 2014 . . .
46
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Implementation
Beispiele:
*Main> sample (choose (’a’,’f’))
’e’
’c’
’d’
’f’
’a’
’a’
’f’
’b’
’d’
’d’
’c’
D. Rösner FP 2014 . . .
47
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Implementation
Kombinatoren, die Generatoren produzieren:
vector . . . produziere einen Generator für Listen mit n
zufälligen Elementen
vector :: Arbitrary a => Int -> Gen [a]
vector n = sequence [ arbitrary | i <- [1..n] ]
D. Rösner FP 2014 . . .
48
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Implementation
die Definition von vector verwendet die allgemeine
monadische Funktion sequence, die aus einer Liste von
Monadeninstanzen eine Monadeninstanz für eine Liste
produziert
im Prelude.hs:
sequence
:: Monad m => [m a] -> m [a]
sequence []
= return []
sequence (c:cs) = do x <- c
xs <- sequence cs
return (x:xs)
D. Rösner FP 2014 . . .
49
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Implementation
variant . . . Kombinator, um zu Generator gezielt
Varianten erzeugen zu können
variant :: Int -> Gen a -> Gen a
variant v (Gen m) = Gen (\n r -> m n (rands r !! (v+1)))
where
rands r0 = r1 : rands r2 where (r1, r2) = split r0
durch wiederholtes Anwenden von split wird aus dem
übergebenen Zufallsgenerator eine Liste von
Zufallsgeneratoren erzeugt, aus der dann das (v+1)-te
Element entnommen und dann im Testgenerator
verwendet wird
D. Rösner FP 2014 . . .
50
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: weitere Aspekte der Implementation
Variation der Standard-Parameter
*Main> :t quickCheckWith
quickCheckWith :: Testable prop => Args -> prop -> IO ()
*Main> quickCheckWith stdArgs { maxSuccess =
*** Failed! Falsifiable (after 110 tests):
[-3,-3,-3]
*Main> quickCheckWith stdArgs { maxSuccess =
*** Failed! Falsifiable (after 607 tests and
[4,4,4]
*Main> quickCheckWith stdArgs { maxSuccess =
*** Failed! Falsifiable (after 408 tests and
[-3,-3,-3]
5000 } prop_RemDup4’’
5000 } prop_RemDup4’’
1 shrink):
5000 } prop_RemDup4’’
1 shrink):
fehlerhafte Implementierung:
remDuplicates []
= []
remDuplicates [x]
= [x]
remDuplicates (x:y:xs) = if x == y then y:remDuplicates xs
else x : remDuplicates (y:xs)
D. Rösner FP 2014 . . .
52
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: weitere Aspekte der Implementation
direkte Implementierung:
remDuplicates []
= []
remDuplicates (x:xs) = if (elem x xs) then remDuplicates xs
else x : remDuplicates xs
*Main> quickCheckWith stdArgs { maxSuccess = 5000 } prop_RemDup4’’
+++ OK, passed 5000 tests:
80% three or more elems, nontrivially all different
5% three or more elems
5% empty list
4% singleton
3% pair list
D. Rösner FP 2014 . . .
53
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: der Datentyp Result
Definition:
data Result
= Result { ok :: Maybe Bool,
stamp :: [String],
arguments :: [String] }
Beispiel:
nothing :: Result
nothing = Result{ ok = Nothing,
stamp = [],
arguments = [] }
D. Rösner FP 2014 . . .
54
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Repräsentation testbarer Prädikate
Typ Property
newtype Property = Prop (Gen Result)
Klasse Testable
class Testable a where
property :: a -> Property
D. Rösner FP 2014 . . .
55
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Repräsentation testbarer Prädikate
zugehörige Funktionen:
result :: Result -> Property
result res = Prop (return res)
evaluate :: Testable a => a -> Gen Result
evaluate a = gen
where Prop gen = property a
D. Rösner FP 2014 . . .
56
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck
Elemente der Sprache für testbare Spezifikationen
forAll
forAll :: (Show a, Testable b)
=> Gen a -> (a -> b) -> Property
forAll gen body = Prop $
do a
<- gen
res <- evaluate (body a)
return (argument a res)
where
argument a res
= res{ arguments = show a : arguments res }
D. Rösner FP 2014 . . .
57
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck
Elemente der Sprache für testbare Spezifikationen
(==>)
(==>) :: Testable a => Bool -> a -> Property
True ==> a = property a
False ==> a = property ()
label
label :: Testable a
=> String -> a -> Property
label s a = Prop (add ‘fmap‘ evaluate a)
where
add res = res{ stamp = s : stamp res }
D. Rösner FP 2014 . . .
58
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck
Elemente der Sprache für testbare Spezifikationen
classify
classify :: Testable a
=> Bool -> String -> a -> Property
classify True name = label name
classify False _
= property
trivial
trivial :: Testable a
=> Bool -> a -> Property
trivial = (‘classify‘ "trivial")
D. Rösner FP 2014 . . .
59
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Literatur: I
Koen Claessen and John Hughes.
Quickcheck: a lightweight tool for random testing of haskell
programs.
In Martin Odersky and Philip Wadler, editors, ICFP, pages
268–279. ACM, 2000.
Bryan O’Sullivan, John Goerzen, and Don Stewart.
Real World Haskell.
O’Reilly Media, Sebastopol, CA 95472, 2009.
ISBN 978-0-596-51498-3; online available at
http://book.realworldhaskell.org/.
D. Rösner FP 2014 . . .
60
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Literatur: II
Simon Thompson.
Haskell - The Craft of Functional Programming.
Addison Wesley Longman Ltd., Essex, 1999.
2nd edition, ISBN 0-201-34275-8; Accompanying Web site:
http://www.cs.ukc.ac.uk/people/staff/sjt/craft2e.
Simon Thompson.
Haskell - the craft of functional programming.
Pearson Education Ltd., Essex, 2011.
3rd edition, ISBN 978-0-201-88295-7; Accompanying Web
site: http://www.haskellcraft.com.
D. Rösner FP 2014 . . .
61
Herunterladen