假设我正在为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
来测试删除已经是给定集合的成员。
答案 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
或setOf1
,setElements
对于简单测试也可能相当简洁:
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
仅用于您实际需要随机选择元素的测试。