I'm new to QuickCheck
and can't quite wrap my head around how to use it.
Let's say I accidentally implemented a data-type with a Set
(instead of a List
):
data Profile = Profile (Set Strategy)
--for completeness:
data Strategy = Strategy Int
and then ran into this bug later, where two objects are equal even if they shouldn't:
Profile (Set.fromList [1,2,3]) == Profile (Set.fromList [2,1,3])
-- D'OH! Order doesn't matter in sets!
How can I write a QuickCheck
test case to test for this case?
In pseudo-code this would look something like this:
assertNotEqual(Profile (Set.fromList [1,2,3]), Profile (Set.fromList [2,1,3]))
assertEqual(Profile (Set.empty), Profile (Set.empty ))
I've tried looking at the examples on the project's github, but it seems they don't cover such trivial cases.
Any hints welcome!
As I commented, my main issue answering this question is the lack of structure around your Profile
type. If you define Profile, a set of operations, and invariants then it becomes easy to make quickcheck tests.
For example, lets say you have a Profile, a way to build profiles, and one way to modify profiles. The properties will all be uniqueness
module Profile (Profile, mkProfile, addItem) where
import Data.Set
newtype Profile = Profile { unProfile :: Set Int }
deriving (Eq, Ord, Show)
mkProfile :: [Int] -> Profile
mkProfile = Profile . fromList
addItem :: Int -> Profile -> Profile
addItem x = Profile . insert x . unProfile
you could test such an ADT with quickcheck by stating properties before and after each operation:
import Test.QuickCheck
import Profile as P
prop_unique_list_unique_profile :: [Int] -> [Int] -> Bool
prop_unique_list_unique_profile xs ys =
xs /= ys ==> mkProfile xs /= mkProfile ys
prop_addItem_nonequal :: Int -> [Int] -> Bool
prop_addItem_nonequal x xs = P.addItem x xs /= xs