Home > database >  How to organize definition with QuickCheck Arbitrary class in Haskell
How to organize definition with QuickCheck Arbitrary class in Haskell

Time:09-15

I am learning to use QuickCheck and find it is a very powerful library. I wrote some toy program with stack, involving a few customized data type and defined all Arbitrary instances in one file in my first run like this and it worked well:

-- MyModule.hs
module MyModule 
  ( A(..)
  , B(..)
  , ...
  ) where

data A = ...
data B = ... -- depends on type A
-- in test program
module TestMyModule where
import Test.QuickCheck
import MyModule 
  ( A
  , B
  )

instance Arbitrary A where
  arbitrary = ...

instance Arbitrary B where
  arbitrary = ... -- depends on arbitrary for type A

prop_A :: A -> Bool 
prop_A = undefined

prop_B :: B -> Bool
prop_B = undefined

main = do
  quickCheck prop_A
  quickCheck prop_B

This works quite well. However when I tried to separate files into two parts, one for A and one for B I ran into issues. I think this was really caused the test program split-up.

-- TestA program
import Test.QuickCheck
import MyModule (A)

instance Arbitrary A where
  arbitrary = ...

... 
-- TestB program
import Test.QuickCheck
import MyModule (A, B)

instance Arbitrary B where
  arbitrary = ... -- code here depends on type A

I got errors like: No instance for (Arbitrary A) arising from a use of ‘quickCheck', which makes sense, because in TestB program, A is not an instance of Arbitrary. But how should I organize the files without putting Arbitrary instance in MyModule itself? I wanted to avoid putting test related definitions in the module.

Thank you in advance.

CodePudding user response:

As Fyodor Soikin writes, you can package these Arbitray instances in a module that you put in your test library.

-- in test program
module MyTests.Arbs with

import Test.QuickCheck
import MyModule

-- Arbitrary instances go here...

-- Import MyTests.Arbs in your other test modules

The compiler ought to complain about orphan instances (it must have done so for the code in the OP as well), so you may want to consider wrapping those types in newtype definitions.

I use a naming scheme for that so that I'd typically have ValidA, InvalidB, AnyB, etc.

  • Related