Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional QuickCheck properties

I wrote a QuickCheck property for a function that merges two sorted inputs into a sorted output:

prop_merge xs ys =
    if (sorted xs && sorted ys) then (sorted (merge xs ys)) else True

That is, when the inputs are sorted, the output is sorted as well. It can also be written as:

prop_merge xs ys = not(sorted xs && sorted ys) || (sorted (merge xs ys))

But I don't really like either version. Is there a nicer syntax for "conditional properties" in QuickCheck?

like image 754
fredoverflow Avatar asked Oct 14 '12 17:10

fredoverflow


People also ask

How does QuickCheck work Haskell?

QuickCheck is a tool for testing Haskell programs automatically. The programmer provides a specification of the program, in the form of properties which functions should satisfy, and QuickCheck then tests that the properties hold in a large number of randomly generated cases.

How do I import a QuickCheck into Ghci?

If you want to use QuickCheck in a command such as stack ghci or stack ghc , you can add it as a --package option e.g. to run a REPL to play around with QuickCheck you can use stack ghci --package QuickCheck and then write import Test. QuickCheck .


1 Answers

You can use the ==> operator to attach a boolean condition to your properties:

prop_merge xs ys = (sorted xs && sorted ys) ==> sorted (merge xs ys)

This is not only a nicer syntax, but allows QuickCheck to differentiate between test cases where the test was successful and test cases which did not satisfy the precondition. In the latter case, the test is not counted and QuickCheck generates new inputs.

However, in cases where most inputs don't satisfy the condition, this will cause your tests to either run more slowly, or, if enough inputs are discarded, QuickCheck will eventually give up. Since a random list is unlikely to be sorted, this is very likely to happen with the example above:

> quickCheck (prop_merge :: [Int] -> [Int] -> Property)
*** Gave up! Passed only 15 tests.

(Note that with standard boolean operators instead of using ==>, QuickCheck will boost about all tests being passed, when most of them were useless due to a failed precondition)

For this reason, it's generally much better to directly generate only the test cases you need. For simple cases, the Test.QuickCheck.Modifiers module contains several useful newtypes which modify the way inputs are generated. For example, the OrderedList modifier will generate only sorted lists, so we can write your property simply as:

prop_merge (Ordered xs) (Ordered ys) = sorted (merge xs ys)
like image 53
hammar Avatar answered Oct 22 '22 02:10

hammar