我正在测试一个生成我自己类型实例的随机生成器。为此,我有一个Arbitrary
的自定义实例:
complexGenerator :: (RandomGen g) => g -> (MyType, g)
instance Arbitrary MyType where
arbitrary = liftM (fst . complexGenerator . mkStdGen) arbitrary
这适用于Test.QuickCheck
(实际上,Test.Framework
),用于测试生成的值是否包含某些属性。但是,我想检查的属性很多,而且我添加的越多,验证它们的时间就越多。
有没有办法使用相同的生成值来测试每个属性,而不是每次都重新生成它们?我显然仍然希望在失败时看到哪个属性不成立,因此使用and
创建一个巨型属性并不是最佳的。
答案 0 :(得分:3)
我显然仍然希望在失败时看到哪些属性不成立,因此使用
and
制作一个巨型属性并非最佳。
您可以在使用printTestCase
创建巨型属性之前使用conjoin
标记每个属性。
e.g。你以为这是一个坏主意:
prop_giant :: MyType -> Bool
prop_giant x = and [prop_one x, prop_two x, prop_three x]
这样会有效,但会给你更好的输出:
prop_giant :: MyType -> Property
prop_giant x = conjoin [printTestCase "one" $ prop_one x,
printTestCase "two" $ prop_two x,
printTestCase "three" $ prop_three x]
(话虽如此,我自己从未使用过这种方法而且只是假设它会起作用; conjoin
可能在文档中被标记为实验性的原因。)
答案 1 :(得分:1)
结合投票的答案,我发现有用的是使用带有Writer monad的Reader变换器:
type Predicate r = ReaderT r (Writer String) Bool
在这种情况下,Reader“共享环境”是经过测试的输入。然后你可以组成这样的属性:
inv_even :: Predicate Int
inv_even = do
lift . tell $ "this is the even invariant"
(==) 0 . flip mod 2 <$> ask
toLabeledProp :: r -> Predicate r -> Property
toLabeledProp cause r =
let (effect, msg) = runWriter . (runReaderT r) $ cause in
printTestCase ("inv: " ++ msg) . property $ effect
并结合:
fromPredicates :: [Predicate r] -> r -> Property
fromPredicates predicates cause =
conjoin . map (toLabeledProp cause) $ predicates
我怀疑还有另一种方法涉及类似于Either或WriterT的东西 - 这将简单地将不同类型的谓词组合成一个结果。但至少,这允许记录属性,这些属性根据输入的值强加不同的后置条件。
编辑:这个想法产生了一个库: http://github.com/jfeltz/quickcheck-property-comb