【问题标题】:How much should one control the `Depth` parameter in smallcheck?smallcheck 中的 `Depth` 参数应该控制多少?
【发布时间】:2013-11-20 09:37:11
【问题描述】:

我正在使用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 具有 changeDepthchangeDepth1 函数,因此我可以将它们用作 changeDepth (const 3) 以确保我的测试始终在合理的时间内运行。但是,通过这样做,我不禁觉得我在某个地方错过了重点?例如,现在不可能通过仅更改命令行选项来运行测试来运行更长的测试,可能在一夜之间。另一方面,如果我使用changeDepth (- 2),我仍然觉得我在假设测试是如何运行的!也许最好假设 5 的全局测试深度在 n 秒内运行,并且由每个属性来调整它认为合适的深度?

很想听听关于smallcheck 这个更实用的方面的反馈。

【问题讨论】:

    标签: haskell smallcheck


    【解决方案1】:

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

    SmallCheck 的不同之处在于您实际上可以推理正在测试的内容。理想情况下,您不应仅将深度视为与您对测试结果的信心相关的指标,而应清楚了解您需要什么深度

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

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

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

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

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

    【讨论】:

      【解决方案2】:

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

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

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

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

      【讨论】:

      • 好的,这很好——我没有考虑完全切换测试范式。你给出了一个非常有说服力的论据,说明为什么 QuickCheck 可能是一个更好的选择。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-04
      • 2012-10-17
      相关资源
      最近更新 更多