Haskell 为类定义一组测试

Haskell 为类定义一组测试,haskell,quickcheck,Haskell,Quickcheck,这个问题继续讨论 我有一个类和该类的一系列实现。大概是这样的: import Test.QuickCheck import Control.Applicative import Test.Framework import Test.Framework.Providers.QuickCheck2 class C c where f :: c -> Int data A = A Int deriving Show instance C A where f (A a) = 2*a

这个问题继续讨论

我有一个类和该类的一系列实现。大概是这样的:

import Test.QuickCheck
import Control.Applicative
import Test.Framework
import Test.Framework.Providers.QuickCheck2

class C c where
  f :: c -> Int

data A = A Int deriving Show

instance C A where
  f (A a) = 2*a

data B = B Int deriving Show

instance C B where
  f (B b) = 2*b
quickCheck (prop_f_is_even :: A -> Property)
我的所有实现都应该满足某个属性。例如:

prop_f_is_even :: C c => c -> Property
prop_f_is_even x = property $ even (f x)
quickCheck prop_f_is_even
我想为每个实现测试该属性。我可以这样做。(我正在使用Test.Framework。)

任意实例,其中
任意的
实例B,其中
任意=B任意
测试::测试
测试=测试组“整束测试”
[
testProperty“prop_f_是偶数-A”(prop_f_是偶数::A->Property),
testProperty“prop_f_是偶数-B”(prop_f_是偶数::B->Property)
--对于所有属性和实现的组合,请继续
]
但在我的例子中,我有几十个属性要测试,还有十几个左右 类,所以这种方法容易出错,而且很麻烦。 (我犯的一个常见错误是剪切和粘贴测试,但忘记更改 类型名称,因此我最终为该属性测试了两次A,而没有测试B。)

我有一个解决方案,我将在下面发布,以防其他人发现它有帮助。

这是我的解决方案

cProperties :: C t => String -> [(String, t -> Property)]
cProperties s = 
  [
    ("prop_f_is_even: " ++ s, prop_f_is_even)
    -- plus any other tests that instances of C should satisfy
  ]

makeTests :: (Arbitrary t, Show t) => [(String, t -> Property)] -> [Test]
makeTests ts = map (\(s,t) -> testProperty s t) ts

aProperties :: [(String, A -> Property)]
aProperties = cProperties "A"

bProperties :: [(String, B -> Property)]
bProperties = cProperties "B"

easierTest :: Test
easierTest = 
  testGroup "tests" (makeTests aProperties ++ makeTests bProperties)
使用这种方法,如果我想添加另一个
C
的所有实例都应该满足的属性,我只需将它添加到
cProperties
。如果我创建另一个
C
实例,称之为
D
,那么我定义
dProperties
,类似于
aProperties
bProperties
,然后更新
easierTest


编辑: 这种方法的一个缺点是cProperties中的所有测试都必须具有类型签名
t->Property
。我自己并没有发现这是一个障碍,因为在我应用这项技术的情况下,出于不相关的原因,我已经定义了一个包含测试所有数据的类型

另一个缺点是,在ghci中,我无法再键入,例如:

prop_f_is_even :: C c => c -> Property
prop_f_is_even x = property $ even (f x)
quickCheck prop_f_is_even
现在我必须键入如下内容:

import Test.QuickCheck
import Control.Applicative
import Test.Framework
import Test.Framework.Providers.QuickCheck2

class C c where
  f :: c -> Int

data A = A Int deriving Show

instance C A where
  f (A a) = 2*a

data B = B Int deriving Show

instance C B where
  f (B b) = 2*b
quickCheck (prop_f_is_even :: A -> Property)