Funktionale Programmierung

Werbung
Quickcheck
Quickcheck
Gliederung
Funktionale Programmierung
1
Fallstudie Quickcheck
D. Rösner
Institut für Wissens- und Sprachverarbeitung
Fakultät für Informatik
Otto-von-Guericke Universität Magdeburg
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
c
Sommer 2014, 20. Juni 2014, 2011
- 14 D.Rösner
D. Rösner FP 2014 . . .
Quickcheck
D. Rösner FP 2014 . . .
1
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Quickcheck
Fallstudie: Testen von Haskell-Programmen
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Fallstudie: QuickCheck
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 . . .
2
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.
4
D. Rösner FP 2014 . . .
5
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Quickcheck
Fallstudie: QuickCheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Fallstudie: QuickCheck
Beispiel: ausführliches Ablaufprotokoll:
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 . . .
Quickcheck
6
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
*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]
Quickcheck
Fallstudie: Testen von Haskell-Programmen
7
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Testen bedingter Eigenschaften
Eigenschaften können die Form haben
<condition> ==> <property>
Programm QuickCheck erlaubt
eine solche Eigenschaft gilt genau dann, wenn die
Eigenschaft nach dem ==> immer gilt, wenn die
<condition> gilt
Testen bedingter Eigenschaften
Testen quantifizierter Eigenschaften
Steuerung der Verteilung der Testfälle
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
Klassifizieren und Zählen von Testfällen
Sammeln von Datenwerten
Arguments exhausted after 97 test.
D. Rösner FP 2014 . . .
9
D. Rösner FP 2014 . . .
10
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Quickcheck
QuickCheck: Testen bedingter Eigenschaften
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Testen quantifizierter Eigenschaften
Eigenschaft:
Eigenschaften können die Form haben
prop_Insert x xs =
ordered xs ==> collect (length xs)$
ordered (insert x xs)
where types = x::Int
forAll <generator> $ \<pattern> -> <property>
Beispiel:
prop_Insert2 x
= forAll orderedList $ \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
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 . . .
Quickcheck
11
D. Rösner FP 2014 . . .
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Quickcheck
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.
12
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
13
D. Rösner FP 2014 . . .
14
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Quickcheck
QuickCheck: Klassifikation von Testfällen
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Klassifikation von Testfällen
Eigenschaften können die Form haben
Verteilung der Testfälle (die mehreren Kategorien
angehören können) wird berichtet
Beispiel:
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]
*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 . . .
Quickcheck
15
D. Rösner FP 2014 . . .
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Quickcheck
QuickCheck: Klassifikation von Testfällen
16
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Sammeln von Datenwerten
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
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
*Main> quickCheck prop_RemDup4’’
*** Failed! Falsifiable (after 10 tests and 2 shrinks):
[-8,-8,-8]
getestet wurde die folgende fehlerhafte Implementierung:
die Verteilung der Werte des Arguments von collect
wird berichtet
-- 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
D. Rösner FP 2014 . . .
18
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Quickcheck
QuickCheck: Sammeln von Datenwerten
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Datenstrukturen
Beispiel:
prop_RevRevColl xs = collect (length xs) $
reverse(reverse xs) == xs
where types = xs::[Int]
Beispiel:
data Card = Card Int String
deriving (Eq,Show)
*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
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 . . .
Quickcheck
D. Rösner FP 2014 . . .
19
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Quickcheck
QuickCheck: Datenstrukturen
21
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Datenstrukturen
Beispiel:
data Info = Number Int | Email String
deriving (Eq,Show)
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"
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
[Tho11], Ch. 19
D. Rösner FP 2014 . . .
22
D. Rösner FP 2014 . . .
23
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Quickcheck
QuickCheck: Datenstrukturen
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 . . .
[Tho11], Ch. 19
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
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
24
D. Rösner FP 2014 . . .
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Quickcheck
QuickCheck: Datenstrukturen
25
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Rekursive 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 . . .
[Tho11], Ch. 19
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
26
D. Rösner FP 2014 . . .
27
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Quickcheck
QuickCheck: Rekursive Datenstrukturen
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Rekursive Datenstrukturen
Implementierung 1
Implementierung 2
arbExpr 0 =
do int <- arbitrary
return (Lit int)
{- reimplementation with frequency and liftM (from Control.Monad) -}
arbExpr 0 = liftM Lit arbitrary
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)
arbExpr
[(1,
(2,
(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
[Tho11], Ch. 19
D. Rösner FP 2014 . . .
Quickcheck
n = frequency
liftM Lit arbitrary),
liftM2 Add subExp subExp),
liftM2 Sub subExp subExp)]
where
subExp = arbExpr (div n 2)
28
D. Rösner FP 2014 . . .
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Quickcheck
QuickCheck: Rekursive Datenstrukturen
29
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Eigenschaften von Funktionen
*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)))
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
[Tho11], Ch. 19
D. Rösner FP 2014 . . .
30
D. Rösner FP 2014 . . .
32
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Quickcheck
QuickCheck: Eigenschaften von Funktionen
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 . . .
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
33
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
*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
Fallstudie: QuickCheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Fallstudie: QuickCheck
Definition als Funktor:
zentral sind Generatoren für Testdaten
instance Functor Gen where
fmap f m = m >>= return . f
Typ:
Definition als Monade:
newtype Gen a
= Gen (Int -> StdGen -> a)
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)
Typ Gen ist Funktor und Monade
D. Rösner FP 2014 . . .
36
D. Rösner FP 2014 . . .
37
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Quickcheck
Fallstudie: QuickCheck
Fallstudie: QuickCheck
Kombinatoren für Generatoren:
Auswahl aus einer Liste von Generatoren
zentrale Funktion:
choose :: Random a => (a, a) -> Gen a
choose bounds = (fst . randomR bounds) ‘fmap‘ rand
oneof :: [Gen a] -> Gen a
oneof gens = elements gens >>= id
Beispiel:
rand :: Gen StdGen
rand = Gen (\n r -> r)
oneof [return True, return False]
D. Rösner FP 2014 . . .
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
38
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
D. Rösner FP 2014 . . .
Quickcheck
Fallstudie: QuickCheck
39
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Fallstudie: QuickCheck
Kombinatoren für Generatoren:
Beeinflussung der Verteilung:
vordefinierte Generatoren für zahlreiche Typen
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
Ü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
Beispiel:
frequency [(2,return True), (1,return False)]
D. Rösner FP 2014 . . .
40
D. Rösner FP 2014 . . .
41
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Quickcheck
Fallstudie: QuickCheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Fallstudie: QuickCheck
vordefinierte Generatoren für zahlreiche Typen
Beispiel: Typ Arbitrary (a, b):
vordefinierte Generatoren für zahlreiche Typen
instance (Arbitrary a, Arbitrary b) => Arbitrary (a, b)
where
arbitrary
= liftM2 (,) arbitrary arbitrary
coarbitrary (a, b) = coarbitrary a . coarbitrary b
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)
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 . . .
Quickcheck
42
D. Rösner FP 2014 . . .
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Quickcheck
QuickCheck: Implementation
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Implementation
Beispiele:
Kombinatoren, die Generatoren produzieren:
elements . . . erzeuge zu einem aus einer Liste zufällig
ausgewählten Element einen Generator
*Main> sample (elements [’a’..’f’])
’c’
’d’
’f’
’d’
’f’
’d’
’d’
’a’
’e’
’e’
’b’
elements :: [a] -> Gen a
elements xs
= (xs !!) ‘fmap‘ choose (0, length xs - 1)
D. Rösner FP 2014 . . .
43
45
D. Rösner FP 2014 . . .
46
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Quickcheck
QuickCheck: Implementation
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Implementation
Beispiele:
Kombinatoren, die Generatoren produzieren:
*Main> sample (choose (’a’,’f’))
’e’
’c’
’d’
’f’
’a’
’a’
’f’
’b’
’d’
’d’
’c’
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 . . .
Quickcheck
47
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
D. Rösner FP 2014 . . .
Quickcheck
QuickCheck: Implementation
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:
variant . . . Kombinator, um zu Generator gezielt
Varianten erzeugen zu können
sequence
:: Monad m => [m a] -> m [a]
sequence []
= return []
sequence (c:cs) = do x <- c
xs <- sequence cs
return (x:xs)
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 . . .
48
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
49
D. Rösner FP 2014 . . .
50
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Quickcheck
QuickCheck: weitere Aspekte der Implementation
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]
direkte Implementierung:
remDuplicates []
= []
remDuplicates (x:xs) = if (elem x xs) then remDuplicates xs
else x : remDuplicates xs
5000 } prop_RemDup4’’
5000 } prop_RemDup4’’
1 shrink):
*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
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 . . .
Quickcheck
D. Rösner FP 2014 . . .
52
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Quickcheck
QuickCheck: der Datentyp Result
53
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck: Repräsentation testbarer Prädikate
Definition:
data Result
= Result { ok :: Maybe Bool,
stamp :: [String],
arguments :: [String] }
Typ Property
newtype Property = Prop (Gen Result)
Klasse Testable
Beispiel:
class Testable a where
property :: a -> Property
nothing :: Result
nothing = Result{ ok = Nothing,
stamp = [],
arguments = [] }
D. Rösner FP 2014 . . .
54
D. Rösner FP 2014 . . .
55
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Quickcheck
QuickCheck: Repräsentation testbarer Prädikate
QuickCheck
Elemente der Sprache für testbare Spezifikationen
forAll
zugehörige Funktionen:
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 }
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 . . .
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
56
D. Rösner FP 2014 . . .
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Quickcheck
QuickCheck
57
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
QuickCheck
Elemente der Sprache für testbare Spezifikationen
(==>)
Elemente der Sprache für testbare Spezifikationen
classify
(==>) :: Testable a => Bool -> a -> Property
True ==> a = property a
False ==> a = property ()
classify :: Testable a
=> Bool -> String -> a -> Property
classify True name = label name
classify False _
= 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 . . .
trivial
trivial :: Testable a
=> Bool -> a -> Property
trivial = (‘classify‘ "trivial")
58
D. Rösner FP 2014 . . .
59
Quickcheck
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Quickcheck
Literatur: I
Einführung
Testen
Datenstrukturen
Funktionen
Generatoren
Implementation
Weitere Aspekte
Literatur: II
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.
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.
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/.
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 . . .
60
D. Rösner FP 2014 . . .
61
Herunterladen