我刚开始使用QuickCheck和一堆Haskell代码。我知道,我落后于时代。这个问题是两个部分:
首先,快速检查的一般最佳做法是什么?到目前为止,我已经了解了以下内容:
X is out of range, Y is in range
if x is out of range, normalize x ≠ x
(或其他一些此类财产)但我仍然在抓住其他最佳做法。特别:
test/
目录中? (如果是,那么如何导入src/
中的内容?)Properties/
下的src
目录中?最重要的是,我们如何倾向于测试类型类的属性?例如,请考虑以下(简化)类型类:
class Gen a where
next :: a -> a
prev :: a -> a
我想测试属性∀ x: prev (next x) == x
。当然,这涉及为每个实例编写测试。为每个实例编写相同的属性是很繁琐的,特别是当测试更复杂时。推广此类测试的标准方法是什么?
答案 0 :(得分:18)
为每个实例编写相同的属性是很乏味的
你不这样做。你为这个类写了一次属性:
class Gen a where
next :: a -> a
prev :: a -> a
np_prop :: (Eq a, Gen a) => a -> Bool
np_prop a = prev (next a) == a
然后要测试它,你会转换为特定类型:
quickCheck (np_prop :: Int -> Bool)
quickCheck (np_prop :: String -> Bool)
您无法提供其他问题。
答案 1 :(得分:10)
我相信来自QC的prop_
约定带有一个脚本,该脚本运行以prop_
开头的所有函数作为测试。所以没有真正的理由这样做,但 在视觉上脱颖而出(因此函数foo
的属性为prop_foo
)。
测试内部结构并没有错。有两种方法:
将属性放在与内部相同的模块中。这使得模块更大,并且需要对项目的QC无条件依赖(除非您使用CPP hackery)。
在非导出模块中具有内部功能,实际导出的功能从另一个模块重新导出。然后,您可以将内部模块导入到定义QC属性的模块中,并且只有在使用.cabal文件中指定的标志时才构建该模块(并且具有QC依赖性)。
如果您的项目很大,那么拥有单独的src/
和test/
目录可能会有用(尽管有区别可能会阻止您测试内部)。但是如果你的项目不是那么大(并且无论如何都位于一个整体模块层次结构下),那么就没有必要像这样拆分它。
正如Norman Ramsey在他的回答中所说,对于类型类,您可以将属性定义为 on 类型类并相应地使用。
答案 2 :(得分:3)
尝试
{-# LANGUAGE GADTs, ScopedTypeVariables #-}
import Test.QuickCheck hiding (Gen)
class Gen a where
next :: a -> a
prev :: a -> a
np_prop :: SomeGen -> Bool
np_prop (SomeGen a) = prev (next a) == a
main :: IO ()
main = quickCheck np_prop
instance Gen Bool where
next True = False
next False = True
prev True = False
prev False = True
instance Gen Int where
next = (+ 1)
prev = subtract 1
data SomeGen where
SomeGen :: (Show a, Eq a, Arbitrary a, Gen a) => a -> SomeGen
instance Show SomeGen where
showsPrec p (SomeGen a) = showsPrec p a
show (SomeGen a) = show a
instance Arbitrary SomeGen where
arbitrary = do
GenDict (Proxy :: Proxy a) <- arbitrary
a :: a <- arbitrary
return $ SomeGen a
shrink (SomeGen a) =
map SomeGen $ shrink a
data GenDict where
GenDict :: (Show a, Eq a, Arbitrary a, Gen a) => Proxy a -> GenDict
instance Arbitrary GenDict where
arbitrary =
elements
[ GenDict (Proxy :: Proxy Bool)
, GenDict (Proxy :: Proxy Int)
]
data Proxy a = Proxy
类型类被引入到存在量化的字典中,在该字典上定义了Arbitrary
实例。然后,此Arbitrary
字典实例用于为存活量化值定义Arbitrary
的实例。
如果您愿意使用ConstraintKinds
,可以进一步推广(并减少样板)。以下仅定义一次。
data Some c where
Some :: (Show a, Arbitrary a, c a) => a -> Some c
instance Show (Some c) where
showsPrec p (Some a) = showsPrec p a
show (Some a) = show a
instance Arbitrary (Dict c) => Arbitrary (Some c) where
arbitrary = do
Dict (Proxy :: Proxy a) :: Dict c <- arbitrary
a :: a <- arbitrary
return $ Some a
shrink (Some a) =
map Some $ shrink a
data Dict c where
Dict :: (Show a, Arbitrary a, c a) => Proxy a -> Dict c
data Proxy a = Proxy
class (c a, d a) => (c &&# d) a
instance (c a, d a) => (c &&# d) a
对于您要测试的每个类型类,都需要Arbitrary
Dict
个实例。
instance Arbitrary (Dict (Eq &&# Gen)) where
arbitrary =
elements
[ Dict (Proxy :: Proxy Bool)
, Dict (Proxy :: Proxy Int)
]
np_prop :: Some (Eq &&# Gen) -> Bool
np_prop (Some a) = prev (next a) == a