我正在寻找一种方法来测试一组确定性值总是的函数(而不是列表中随机选择的值)。例如,我想要一种检查
的方法f :: (Num a) => a -> Bool
f x = (x - 2) == (x-3+1)
完全适用于Int
的1,3和5,没有明确写出
testGroup "f tests" $ map (testProperty . property) [f (1::Int), f (3::Int), f (5::Int)]
相反,我希望Int
周围有一些包装器类型,这样当我在此类型上调用f
时,它会每次使用值1,3和5测试f
这样做的动机是this answer来测试多态函数。
我想出了一个使用我自己的Arbitrary [Some c]
实例的解决方案:
data Some c where
Some :: (Show a, Arbitrary a, c a) => a -> Some c
instance Arbitrary [Dict c] => Arbitrary [Some c] where
arbitrary = do
dicts :: [Dict c] <- arbitrary
sequence $ map (\(Dict (Proxy::Proxy a)) -> liftM Some (arbitrary :: Gen a)) dicts
data Dict c where
Dict :: (Show a, Arbitrary a, c a)
=> Proxy a -> Dict c
class ClassToTest a
-- where ...
instance Arbitrary [Dict ClassToTest] where
arbitrary = return $ [Dict (Proxy::Proxy TYPE1),
Dict (Proxy::Proxy TYPE2)]
testAll :: forall a . (Arbitrary [a]) => (a -> Bool) -> Gen Bool
testAll f = do
xs' <- arbitrary :: Gen [a]
return $ and $ map f xs'
然后我可以写一个函数
myTest :: (ClassToTest a) => a -> Bool
myTest x = error ""
theTest :: Test
theTest = testProperty "mytest" $ testAll myTest
会生成随机值TYPE1
并运行myTest
,然后生成TYPE2
的随机值并再次运行myTest
。这个想法是TYPE *的列表非常大,所以我宁愿不依赖于随机选择来确保列表中的所有内容都被测试。我的方法问题当然是QuickCheck已经有(Arbitrary a) => Arbitrary [a]
的通用实例,所以这段代码需要-XOverlappingInstances
。有更好的方式吗?