Warm tip: This article is reproduced from stackoverflow.com, please click
haskell quickcheck

How to write EqProp instances for quickBatch tests?

发布于 2020-04-10 10:07:56

I've come across 2 examples:

data List a = 
    Nil 
  | Cons a (List a) 
  deriving (Eq, Show) 

instance Eq a => EqProp (List a) where
  (=-=) = eq
newtype ZipList' a = 
  ZipList' (List a) 
  deriving (Eq, Show)

instance Eq a => EqProp (ZipList' a) where 
  xs =-= ys = xs' `eq` ys' 
    where xs' = let (ZipList' l) = xs 
                in take' 3000 l 
          ys' = let (ZipList' l) = ys 
                in take' 3000 l 

take' :: Int -> List a -> List a
take' n (Cons a as) = Cons a (take' (n - 1) as)

(There are also Functor and Applicative instances, which are being tested):

main :: IO ()
main =
  hspec $ do
    describe "List" $ do
      it "Functor" $
        property (quickBatch $ functor (undefined :: List (Int, Float, String)))
      it "Applicative" $
        property
          (quickBatch $ applicative (undefined :: List (Int, Float, String)))
    describe "ZipList'" $ do
      it "Functor" $
        property
          (quickBatch $ functor (undefined :: ZipList' (Int, Float, String)))
      it "Applicative" $
        property
          (quickBatch $ applicative (undefined :: ZipList' (Int, Float, String)))

As I understand, we take 3000 values, because otherwise tests will chase the infinite lists forever. But in the first example, there is an infinite list too, isn't there? So why is EqProp trivial in this case?

Questioner
kirill fedorov
Viewed
83
Fresheyeball 2020-02-01 20:14

Your property testing library (I know quickCheck, but not quickBatch) should be managing data size when values are generated, not when values are compared. Limiting the size in your equality check is a bad idea, because it means that

  1. Data generation can be infinite, so your test can be non terminating BEFORE it gets to the equality check
  2. You can have false positives where the 3001st element differs

Since there is already and instance for [] and ZipList is a newtype we can get the instance for free like so.

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE StandaloneDeriving #-}

deriving instance Eq a => EqProp (ZipList' a)