Search code examples
haskellquickcheck

Testing not equal in Quickcheck?


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!


Solution

  • 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