5

我正在做我的第一次真正的工作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

框架中的默认smallcheck深度tasty是 5,这导致我还没有看到完成的测试运行。smallcheck具有changeDepthandchangeDepth1功能,因此我可以使用它们changeDepth (const 3)来确保我的测试始终在合理的时间内运行。但是,通过这样做,我不禁觉得我在某个地方错过了重点?例如,现在不可能通过仅更改命令行选项来运行测试来运行更长的测试,可能在一夜之间。另一方面,如果我使用changeDepth (- 2),它仍然感觉好像我在假设测试是如何运行的!也许最好假设 5 的全局测试深度在n秒内运行,并且由每个属性来调整它认为合适的深度?

希望听到一些关于smallcheck.

4

2 回答 2

5

当您使用 QuickCheck 的随机测试进行测试时,您拥有的唯一指标是测试的数量,因此自然要尽可能多地进行测试。

SmallCheck 的不同之处在于您实际上可以推断正在测试的内容。理想情况下,您不应将深度视为与您对测试结果的信心相关的指标,但您应该对自己需要的深度有一个很好的了解

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

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

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

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

  • 数字溢出
  • 未发现的任意硬编码限制(可能在外国 C 代码中——这是非常不典型的 Haskell 代码)
于 2013-12-09T11:15:51.137 回答
3

虽然 smallcheck 的“详尽性”(无论如何都是小案例)是一个有趣的属性,但我宁愿在这种情况下建议 quickcheck。虽然 JSON 具有轻量级结构,但从实际数据位的角度来看,它是相当沉重的。

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

换句话说,如果您平均有 2 个构造函数,那么您正在查看类似 32 个要运行的测试用例,但如果您有 20 个,则更像是 3200000 个。

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

于 2013-11-20T10:48:46.350 回答