MathematicalOrchid
MathematicalOrchid

Reputation: 62848

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...

Upvotes: 3

Views: 145

Answers (2)

MathematicalOrchid
MathematicalOrchid

Reputation: 62848

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.)

Upvotes: 4

Yuriosity
Yuriosity

Reputation: 1918

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)

Upvotes: 4

Related Questions