Haskell smallcheck中的'Depth'参数应该控制多少?

Haskell smallcheck中的'Depth'参数应该控制多少?,haskell,smallcheck,Haskell,Smallcheck,我正在用smallcheck做第一点实际工作,我对如何使用Depth参数有点困惑。在开始之前,让我先说明一下我使用的是什么smallcheck 在工作中,我们正在自己的内部数据库前面构建一个简单的web服务。web服务执行一些查询,并使用序列化为JSON的查询结果进行响应。我目前正在做的是确保:给定一个表示查询结果的对象,该对象将生成预期的JSON。例如: data Action = Action { actionType :: !ActionType , actio

我正在用
smallcheck
做第一点实际工作,我对如何使用
Depth
参数有点困惑。在开始之前,让我先说明一下我使用的是什么
smallcheck

在工作中,我们正在自己的内部数据库前面构建一个简单的web服务。web服务执行一些查询,并使用序列化为JSON的查询结果进行响应。我目前正在做的是确保:给定一个表示查询结果的对象,该对象将生成预期的JSON。例如:

data Action
  = Action { actionType :: !ActionType 
           , actionDescription :: !Text
           , actionPerformedAt :: !UTCTime
           , actionAgentName :: !Text
           }
必须生成JSON,例如:

{
  "type": "Booking",
  "description": "Whatever",
  "performedAt": "2012-01-04",
  "agent": "Tom"
}
这看起来是
smallcheck
的理想任务,我将其表述如下:

testAction :: Tasty.TestTree
testAction = Tasty.testGroup "Action"
  [ SmallCheck.testProperty "type" $
      SmallCheck.over actions $ match $
        Aeson.key "type" --> Aeson.toJSON . actionType

  , SmallCheck.testProperty "dateActioned" $
      SmallCheck.over actions $ match $
        Aeson.key "dateActioned" --> expectedUTCTimeEncoding . actionPerformedAt

  -- and so on
  ]

-- (-->) :: Eq a => lens-aeson traversal a -> (b -> a) -> b -> Bool
-- actions :: Monad m => SmallCheck.Series m Action
tasty
框架中,默认的
smallcheck
深度为5,这导致测试运行尚未完成
smallcheck
具有
changeDepth
changeDepth1
功能,因此我可以将它们用作
changeDepth(const 3)
以确保测试始终在合理的时间内运行。然而,通过这样做,我忍不住觉得我在什么地方遗漏了要点?例如,现在仅通过更改命令行选项来运行测试,不可能运行更长的测试,可能是一夜之间。另一方面,如果我使用了
changeDepth(-2)
,仍然感觉好像我在假设测试是如何运行的!也许最好假设一个5的全局测试深度在n秒内运行,并且由每个属性根据需要调整深度

我很想听到一些关于
smallcheck

更实用方面的反馈。虽然smallcheck的“穷尽性”(无论如何,对于小案例)是一个有趣的特性,但我还是建议在这种情况下使用quickcheck。虽然JSON具有轻量级结构,但从实际数据位的角度来看,它相当重

测试时间也非常关键地取决于您如何在类型的系列实例中定义smallcheck的“大小”(深度)。如果您的类型有很多分支(许多构造函数),那么测试的数量将快速增长。它在“深度”上是指数的,而指数的基础是与特定测试用例相关的系列实例中的分支数量

换句话说,如果你平均有2个构造函数,你需要运行32个测试用例,但是如果你有20个,它更像是3200000


然而,您的覆盖率也会受到影响——如果您减少测试用例中的分支(使深度增加得更快),那么在给定深度下,您将获得更少的覆盖率。使用quickcheck,您可以牺牲一些“小”测试用例,而选择一些使用smallcheck无法获得的更大示例进行采样。

当您使用quickcheck的随机测试进行测试时,唯一的度量是测试的数量,因此尽可能多的测试是很自然的

SmallCheck的不同之处在于,您实际上可以说明测试内容的原因。理想情况下,您不应该将深度仅仅视为与您对测试结果的信心相关的一个指标,但您应该对您需要的深度有一个好主意

如果我们谈论的是JSON,那么大多数处理JSON的函数一次只能使用一层或两层结构。因此,如果有bug,粗略地说,它可以在深度为2或3的结构上被发现。(您需要根据
Serial
实例查找或计算smallcheck的深度,该深度将为您提供所需的结构深度。)

因此,为了回答您的问题,如果深度3是您能负担得起的最大值,那么首先您应该确定这对于您正在测试的代码类型是否足够

如果刚好不够,那么您可以用宽度换取深度(例如,通过减少叶值的深度),或者切换到QuickCheck的随机枚举策略

我认为只有当您觉得您正在测试的函数可能由于结构的大小而存在bug时,才应该使用QuickCheck,而不是结构组件的某些局部组合。我能想到的一些例子是:

  • 数字溢出
  • 未发现的任意硬编码限制(可能在外国C代码中-这是Haskell代码的一个非常非典型)

好的,这是一个很好的观点-我没有考虑完全转换测试范式。你给出了一个很有说服力的论据,为什么快速检查可能是一个更好的选择。