快速检查:生成任意集的任意元素

时间:2016-02-20 20:44:01

标签: haskell quickcheck

假设我正在为Data.Set编写测试。我想检查删除集合中的元素是否有效,所以我可能会这样写:

prop_deleteA it x = member x it ==> not (member x (delete x it))

假设it具有合适的Arbitrary实例。但是,这依赖于quickcheck生成的x值恰好存在于集合中,这通常不能保证。如果x可以依赖it以保证x已经是it的成员,那会更好。我怎么能这样做?

我以为我可以写

prop_deleteB it f = let x = f it
                   in not (member x (delete x it))

其中f :: Set a -> a通过共同目的适当地定义。但是,coarbitrary只允许我们定义f :: Set a -> b,遗憾的是它不是我们想要的。到目前为止,我最好的想法是定义一个新类型

data SetAndElement a = SetAndElement (Set a) a

允许我们编写合适的Arbitrary实例

instance (Ord a, Arbitrary a) => Arbitrary (SetAndElement a) where
    arbitrary = do it <- suchThat arbitrary (not . Set.null)
                   x  <- elements (elems it)
                   return (SetAndElement it x)

允许将prop_delete写为

prop_deleteC (SetAndElement it x) = not (member x (delete x it))

这有效,但似乎有点牵扯;还有更好的选择吗? (如果没有,我将修改问题并将其作为答案。)实际Data.Set实现(容器包)通过检查(delete x) . (insert x) == id x是否manage.py migrate来测试删除已经是给定集合的成员。

1 个答案:

答案 0 :(得分:4)

这取决于您有哪些发电机。例如,如果您已经setOf1(生成Set至少包含一个元素)和setElements(从Set获取元素),则可以使用{ {1}}:

forAll

这与-- example implementations of both combinators setOf1 :: (Arbitrary a, Ord a) => Gen a -> Gen (Set a) setOf1 = fmap fromList . listOf1 setElements :: Set a -> Gen a setElements = elements . toList prop_delete = forAll (setOf1 arbitrary) $ \theSet -> forAll (setElements theSet) $ \x -> not (member (x :: Int) (delete x theSet)) 大致相同,但我们使用的是可用于进一步测试的可重用函数,而不是固定的SetAndElement类型:

data

但是,即使您不写prop_null = forAll (setOf1 (arbitrary :: Gen Integer)) $ not . null setOf1setElements对于简单测试也可能相当简洁:

forAll

如果您提供prop_delete :: (Arbitrary a, Ord a) => (NonEmptyList a) -> Property prop_delete (NonEmpty xs) = let theSet = fromList xs in forAll (elements xs) $ \x -> not (member x (delete x theSet)) setElements,则可以将其写为

NonEmptySet

这样,您可以将newtype NonEmptySet x = NonEmptySet {getNonEmptySet :: Set a} instance (Ord a, Arbitray a) => Arbitrary (NonEmptySet a) where arbitrary = fmap NonEmptySet . setOf1 $ arbitrary prop_delete :: (Arbitrary a, Ord a) => (NonEmptySet a) -> Property prop_delete (NonEmptySet theSet) = forAll (setElements theSet) $ \x -> not (member x (delete x theSet)) 用于需要非空集的测试,而NonEmptySet仅用于您实际需要随机选择元素的测试。