Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generate new test data inside QuickCheck property

I'm having trouble with a programming problem here. Half the trouble is that the problem itself is quite tricky to think about, and the other half is that I can't remember how to find my way around QuickCheck.

I know that if you write a function that takes several arguments that have an Arbitrary instance, QuickCheck will let you use that method as a test. What I can't figure out is how to generate new test arguments inside that method. I want to write something like

prop13 :: Foo -> Bar -> Bool
prop13 foo bar =
  if foobar foo bar
    then fn1 foo
    else newInput $ \ baz -> fn2 foo bar baz

but I can't figure out how the hell to do that.

Actually, no, what I really want to write is

prop13 :: Foo -> Bar -> Property
prop13 foo bar =
  if foobar foo bar
    then label "foobar=YES" $ fn1 foo
    else label "foobar=NO"  $ newInput $ \ baz -> fn2 foo bar baz

just so I can check it isn't taking one branch 100% of the time or something ridiculous like that.

Actually, what would be great is if I could demand that baz has some particular property. I vaguely remember QuickCheck having a function somewhere to throw away inputs not satisfying a given condition. (The only problem being that it might take an unreasonably number of attempts to satisfy the condition...)

Is there a way to do this? I'm staring at the Haddock page, but I can't figure out how to get what I want...

like image 782
MathematicalOrchid Avatar asked Dec 17 '25 13:12

MathematicalOrchid


2 Answers

A property may take the form

classify <condition> <string>$ <property>

For example,

prop_Insert x xs = ordered xs ==> classify (ordered (x:xs)) "at-head" $ classify (ordered (xs ++ [x])) "at-tail" $ ordered (insert x xs) where types = x :: Int

Test cases satisfying the condition are assigned the classification given, and the distribution of classifications is reported after testing. In this case the result is

Main> quickCheck prop_Insert OK, passed 100 tests. 58% at-head, at-tail. 22% at-tail. 4% at-head.

Note that a test case may fall into more than one classification.

(from QuickCheck manual)

For demanding the particular property of the input data, you may add somePredicate data ==> before the test body, as it is shown in the snippet above. Another example:

prop_max xs = (not . null xs) ==> head (sort xs) == maximum xs

You're right, this combinator throws away inappropriate cases. If that's undesired, you can make a newtype wrapper over the input type and redefine the Arbitrary instance for it (see examples of Positive, NonEmpty and so on here)

like image 79
Yuuri Avatar answered Dec 20 '25 01:12

Yuuri


I found the answer in another answer. Apparently it's forAll:

else forAll arbitrary $ \ baz -> fn2 foo bar baz

I just couldn't remember how to do it...

(This also has the nice feature of allowing me to specify a specific random data generator.)

like image 45
MathematicalOrchid Avatar answered Dec 20 '25 01:12

MathematicalOrchid



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!