Question

I have a function with the following type signature

rndListIndex :: Double -> Double -> Double -> Double
rndListIndex maxIdx r1 r2 = …
  • the first input should be a value coming from a non-negative strictly positive integer
  • the second and third input are required to be within the closed interval [0.0,1.0] otherwise the function makes no sense

the function has the property of

prop_alwaysLessThanMaxIdx idx r1 r2 = (rndListIndex idx r1 r2 <= idx)

how do I generate random data for maxIdx and r1,r2 separately; I know of the function choosebut do not know how to use it with more than one input variable.

For now I have tested the Property with fixed idx, which is not the way it should be tested.

Was it helpful?

Solution

You have to use the forAll function from QuickCheck. It has the following type:

forAll :: (Show a, Testable prop) 
       => Gen a           -- ^ The generator to use for generating values
       -> (a -> prop)     -- ^ A function which returns a testable property
       -> Property                  

forAll takes two arguments:

  • The generator describes how to generate values. Examples of generators are choose, arbitrary, oneof, ...
  • The function tests the property for the given input. It must return a value that is an instance of Testable, for example another Property, Bool or a function.

Example of a nested forAll with the choose and elements generators:

-- This generates a Property p for all x's in the closed interval [1,3]
-- The property p in turn generates a property q for all y ∈ [4,5]
-- The property q is True if x < y.
prop_choose = forAll (choose (1,3)) $ \x ->
              forAll (elements [4,5]) $ \y -> x < y

For your test property, you can use forAll with choose for the second and third argument. For the first argument, there is the Positive a type in QuickCheck which can be used to generate arbitrary positive values of type a (It has an Arbitrary instance when a is a Num):

prop_alwayLessThanMaxIdx :: Positive Integer -> Property
prop_alwaysLessThanMaxIdx (Positive idx) = 
  forAll (choose (0,1)) $ \r1 ->
  forAll (choose (0,1)) $ \r2 ->
   (rndListIndex idx r1 r2) < idx

OTHER TIPS

I would suggest defining your own type that wraps Double and give it an Arbitrary instance that only generates numbers between 0 and 1. Something like:

import Test.QuickCheck
newtype UnitInterval = Unit Double deriving Show

instance Arbitrary UnitInterval where
  arbitrary = fmap Unit (choose (0, 1))
  shrink (Unit x) = [ Unit y | y <- shrink x, 0 <= y && y <= 1 ]

For generating idx, you can use QuickCheck's Positive modifier, as @bennoffs suggested (you will have to import Test.QuickCheck.Modifiers). This is similar to the UnitInterval type I defined above, but generates positive numbers instead of numbers between 0 and 1. Your property will then look like:

prop_alwaysLessThanMaxIdx (Positive idx) (Unit r1) (Unit r2) =
  rndListIndex idx r1 r2 <= idx
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top