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