在smallcheck中控制`Depth`参数应该多少?

时间:2013-11-20 09:37:11

标签: haskell smallcheck

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

很想听到smallcheck这个更实际的方面的一些反馈。

2 个答案:

答案 0 :(得分:5)

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

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

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

所以,要回答你的问题,如果深度3是你能承受的最大值,那么首先你应该决定这对你正在测试的代码类型是否足够

如果它不合适,那么您可以交换广度以获得深度(例如通过减小叶值的深度),或者确实切换到QuickCheck的随机枚举策略。

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

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

答案 1 :(得分:3)

虽然smallcheck的“穷举”(无论如何对于小案例)是一个有趣的属性,但我宁愿建议在这种情况下快速检查。虽然JSON具有轻量级结构,但从实际数据位的角度来看,它非常重要。

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

换句话说,如果你平均有2个构造函数,你会看到像32个测试用例一样运行,但是如果你有20个,它就更像是3200000。

然而,您的覆盖范围也会受到影响 - 如果您在测试用例中减少分支(使深度增加更快),您将获得更少的覆盖深度。通过快速检查,您可以交易丢失一些“小”测试用例,而不是使用smallcheck无法获得的一些更大的示例。