我有以下数据和功能
data Foo = A | B deriving (Show)
foolist :: Maybe Foo -> [Foo]
foolist Nothing = [A]
foolist (Just x) = [x]
prop_foolist x = (length (foolist x)) == 1
运行quickCheck prop_foolist
时,ghc告诉我Foo需要是一个任意的实例。
No instance for (Arbitrary Foo) arising from a use of ‘quickCheck’
In the expression: quickCheck prop_foolist
In an equation for ‘it’: it = quickCheck prop_foolist
我尝试了data Foo = A | B deriving (Show, Arbitrary)
,但结果是
Can't make a derived instance of ‘Arbitrary Foo’:
‘Arbitrary’ is not a derivable class
Try enabling DeriveAnyClass
In the data declaration for ‘Foo’
但是,我无法弄清楚如何使DeriveAnyClass
成为可能。我只是想用我的简单功能快速检查! x的可能值是Nothing,Just A和Just B.当然这应该可以测试吗?
答案 0 :(得分:5)
有两种合理的方法:
如果有另一个看起来相似的实例,您可以使用它。 Gen
类型是Functor
,Applicative
甚至Monad
的实例,因此您可以轻松地从其他类型构建生成器。这可能是编写Arbitrary
实例的最重要的一般技术。大多数复杂实例将由一个或多个更简单的实例构建。
boolToFoo :: Bool -> Foo
boolToFoo False = A
boolToFoo True = B
instance Arbitrary Foo where
arbitrary = boolToFoo <$> arbitrary
在这种情况下,Foo
无法&#34;缩小&#34;以任何有意义的方式对子部分,因此shrink
的默认普通实现将正常工作。如果它是一个更有趣的类型,你可以使用一些类似的
shrink = map boolToFoo . shrink . fooToBool
Test.QuickCheck.Arbitrary
和/或Test.QuickCheck.Gen
在这种情况下,将各个部分放在一起非常容易:
import Test.QuickCheck.Arbitrary
data Foo = A | B
deriving (Show,Enum,Bounded)
instance Arbitrary Foo where
arbitrary = arbitraryBoundedEnum
如前所述,在这种情况下,默认的shrink
实现会没问题。在递归类型的情况下,您可能想要添加
{-# LANGUAGE DeriveGeneric #-}
import GHC.Generics (Generic)
然后为您的类型派生Generic
并使用
instance Arbitrary ... where
...
shrink = genericShrink
正如文档所警告的那样,genericShrink
不尊重您可能希望施加的任何内部有效性条件,因此在某些情况下可能需要一些注意。
你问过DeriveAnyClass
。如果你想要,你可以添加
{-# LANGUAGE DeriveAnyClass #-}
到文件的顶部。但你不想要那个。无论如何,你当然不想要它。它仅适用于具有基于泛型的完整默认值的类,通常使用DefaultSignatures
扩展名。在这种情况下,default arbitrary :: Generic a => Gen a
类定义中没有Arbitrary
行,arbitrary
是必需的。因此,只要QuickCheck尝试调用其Arbitrary
方法,DeriveAnyClass
生成的arbitrary
实例就会产生运行时错误。