Skip to content

ChandraKoduru/how-to-quickcheck

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

how-to-quickcheck

https://begriffs.com/posts/2017-01-14-design-use-quickcheck.html

Basically I need to construct generators (Gen a) for all the types for which I need random samples for testing.

generate :: Gen a -> IO a

The generate function takes in a 'generator' and yields random samples

Another utility function that could be useful in viewing samples generated by generator.

sample :: Gen a -> IO () sample' :: Gen a -> IO [a]

The above just calls the repeatedly generator and prints the output to the stdio.

sample :: Gen a -> IO () sample gen_a = sample' gen_a 10 where sample' gen_a n | n <= 0 = return () | otherwise = do a <- generate gen_a putStrLn (show a) sample' gen_a (n-1)

Making generators

Using combinators

choose :: (a, a) -> Gen a elements :: [a] -> Gen a

Becoming an instance of Arbitrary type class.

class Arbitrary a where arbitrary :: Gen a

generate $ choose (1, 2) generate $ elements [1,2,3] generate $ (arbitrary :: Gen Int)

It is simpler, easier in practice to make instances of the Arbitrary for our data types.

Becoming instances of Arbitrary

There is already enough supply of helper functions constructing arbitrary instances for recursive structures of basic types eg. Bool, Int, Char, String, Tuple, Lists, Maybe, Either etc..

Using applicative(<$>) and functor (<*>) since Gen is a Monad -> Applicative -> Functor (excluding the monadic laws)

data MyType = MyType { foo :: Int, , bar :: Bool, , baz :: Char } deriving (Show)

myTypeGenerator :: Gen MyType myTypeGenerator = MyType <$> arbitrary <> arbitrary <> arbitrary

generate myTypeGenerator :: IO MyType

Finer control to generate the samples

oneof :: [Gen a] -> Gen a -- almost even distribution among the available generators frequency :: [(Int, Gen a)] -> Gen a -- frequency control/mixing sized :: (Int -> Gen a) -> Gen a -- controlling the size of the generated sample

Properties

Properties are predicates that can be checked by testing. QuickCheck represents them am Gen Result A property is a 'Gen Result' i.e generates results. All these results need to be tested OK for the property to stand good. quickCheck runs the 'property' multiple times and when all the invocations succeed, the property is considered passed.

The 'Result' object holds the facts about a test including ...

  • success/failed
  • retry test
  • statistics about test cases
  • reason for failure
  • exceptions thrown

prop_commutativeAdd :: Gen Result prop_commutativeAdd = do (x, y) <- arbitrary :: Gen (Int, Int) return $ if x + y == y + x then succeeded else failed { reason = "Addition is not commutative" }

with quickCheck (Testable prop => prop -> IO ())

we can construct and invoke all testable.

Any instance of Arbitrary can generate instance of a.

Similarly...Any instance of Testable can generate a Property

Gen a ---> Arbitrary Property ---> Testable

Refer test/Spec.hs. It has all the ways a 'Testable' can be constructed.

Test case distribution

Refer : test/Base64Test.hs

Properties for testing base64 encoding.

Using collect, classify and cover

collect :: Is useful to collect the distribution of test case inputs classify :: Helps in bucketizing, based on how we configure and displaying the distribution in those buckets cover :: We case use this to force a failure when enough sample are generated to cover all edge cases, that we are wanting. scale :: To scale up/down a generator (Gen a) by a factor (function signature: (Int -> Int) -> Gen a -> Gen a) forAll : To use a custom generator

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published