Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Testing Haskell QuickCheck最佳实践(尤其是在测试类型类时)_Testing_Haskell - Fatal编程技术网

Testing Haskell QuickCheck最佳实践(尤其是在测试类型类时)

Testing Haskell QuickCheck最佳实践(尤其是在测试类型类时),testing,haskell,Testing,Haskell,我刚刚开始使用QuickCheck和一堆Haskell代码。我知道,我落后于时代。这个问题分为两部分: 首先,快速检查的一般最佳实践是什么?到目前为止,我已经了解了以下内容: 将您的测试命名为prop_*(恼人,因为其他一切都是如此) 测试导出的代码(如果您正在测试内部代码,那么很可能是做错了) 测试属性,而不是示例 不要说X超出范围,Y在范围内 相反,如果x超出范围,则说,将x规格化≠ x(或其他类似属性) 但我仍在掌握其他最佳实践。特别是: 财产存放在哪里? 同一个文件 在tes

我刚刚开始使用QuickCheck和一堆Haskell代码。我知道,我落后于时代。这个问题分为两部分:

首先,快速检查的一般最佳实践是什么?到目前为止,我已经了解了以下内容:

  • 将您的测试命名为prop_*(恼人,因为其他一切都是如此)
  • 测试导出的代码(如果您正在测试内部代码,那么很可能是做错了)
  • 测试属性,而不是示例
    • 不要说X超出范围,Y在范围内
    • 相反,如果x超出范围,则说
      ,将x规格化≠ x
      (或其他类似属性)
但我仍在掌握其他最佳实践。特别是:

  • 财产存放在哪里?
    • 同一个文件
    • test/
      目录中?(如果是这样,那么如何在
      src/
      中导入内容?)
    • src
      下的
      Properties/
      目录中
最重要的是,我们倾向于如何在类型类上测试属性?例如,考虑下面的(简化)类型类:

class Gen a where
    next :: a -> a
    prev :: a -> a
我想测试属性
∀ x:prev(next x)=x
。当然,这涉及到为每个实例编写测试。为每个实例编写相同的属性是很乏味的,尤其是当测试更复杂时。推广此类测试的标准方法是什么

为每个实例编写相同的属性是乏味的

你不能这样做。为类编写一次属性:

class Gen a where
    next :: a -> a
    prev :: a -> a

np_prop :: (Eq a, Gen a) => a -> Bool
np_prop a = prev (next a) == a
然后,为了测试它,您将强制转换为特定类型:

quickCheck (np_prop :: Int -> Bool)
quickCheck (np_prop :: String -> Bool)

你的其他问题我帮不上忙。

我相信
prop\u
惯例来自QC,它附带了一个脚本,可以运行以
prop\u
作为测试开始的所有函数。因此没有真正的理由这么做,但它确实在视觉上很突出(因此函数
foo
的属性是
prop\u foo

测试内部构件并没有什么问题。有两种方法可以做到这一点:

  • 将属性与内部构件放在同一模块中。这使得模块更大,并且要求项目无条件地依赖于QC(除非您使用CPP hackery)

  • 在非导出模块中具有内部构件,实际要导出的函数从另一个模块重新导出。然后,您可以将内部模块导入到定义QC属性的模块中,并且该模块仅在使用.cabal文件中指定的标志时生成(并且具有QC依赖项)

如果您的项目很大,那么拥有单独的
src/
test/
目录可能会很有用(尽管有区别可能会阻止您测试内部构件)。但是,如果您的项目没有那么大(并且始终位于一个整体模块层次结构下),那么就没有必要像那样将其拆分

正如Norman Ramsey在他的回答中所说,对于类型类,您可以将属性定义为在类型类上,并相应地使用它。

试试看

{-# LANGUAGE GADTs, ScopedTypeVariables #-}
import Test.QuickCheck hiding (Gen)

class Gen a where
  next :: a -> a
  prev :: a -> a

np_prop :: SomeGen -> Bool
np_prop (SomeGen a) = prev (next a) == a

main :: IO ()
main = quickCheck np_prop

instance Gen Bool where
  next True = False
  next False = True
  prev True = False
  prev False = True

instance Gen Int where
  next = (+ 1)
  prev = subtract 1

data SomeGen where
  SomeGen :: (Show a, Eq a, Arbitrary a, Gen a) => a -> SomeGen

instance Show SomeGen where
  showsPrec p (SomeGen a) = showsPrec p a
  show (SomeGen a) = show a

instance Arbitrary SomeGen where
  arbitrary = do
    GenDict (Proxy :: Proxy a) <- arbitrary
    a :: a <- arbitrary
    return $ SomeGen a
  shrink (SomeGen a) =
    map SomeGen $ shrink a

data GenDict where
  GenDict :: (Show a, Eq a, Arbitrary a, Gen a) => Proxy a -> GenDict

instance Arbitrary GenDict where
  arbitrary =
    elements
    [ GenDict (Proxy :: Proxy Bool)
    , GenDict (Proxy :: Proxy Int)
    ]

data Proxy a = Proxy
对于要测试的每个类型类,都需要一个
Dict
任意
实例

instance Arbitrary (Dict (Eq &&# Gen)) where
  arbitrary =
    elements
    [ Dict (Proxy :: Proxy Bool)
    , Dict (Proxy :: Proxy Int)
    ]

np_prop :: Some (Eq &&# Gen) -> Bool
np_prop (Some a) = prev (next a) == a

对于使用并行
src/
test/
目录时的导入,您需要在
.cabal
文件中设置
Hs-Source-Dirs:src,test
,以便两个目录都位于模块搜索路径中。为什么内部不能有属性?它们当然可以,只是很难对它们进行测试(根据我的经验)测试导出的行为比测试实现细节有用得多。我们经常做的事情是有一个Cabal
测试套件
部分,它直接依赖于内部模块(而不是在同一个.Cabal文件中定义的库),这样您就可以测试这些模块,而需要花费一些额外的编译时间。
instance Arbitrary (Dict (Eq &&# Gen)) where
  arbitrary =
    elements
    [ Dict (Proxy :: Proxy Bool)
    , Dict (Proxy :: Proxy Int)
    ]

np_prop :: Some (Eq &&# Gen) -> Bool
np_prop (Some a) = prev (next a) == a