【问题标题】:How to display a reason of a failed test property with quickcheck?如何使用快速检查显示测试属性失败的原因?
【发布时间】:2011-01-23 08:17:20
【问题描述】:

在通过 QuickCheck 测试属性测试失败时显示失败原因的最佳做法是什么?

举个例子:

prop a b = res /= []
   where
      (res, reason) = checkCode a b

那么会话可能如下所示:

> quickCheck prop
Falsifiable, after 48 tests:
42
23

但对于调试而言,将失败原因显示为 quickCheck 可伪造报告的一部分会非常方便。

我是这样破解的:

prop a b = if res /= [] then traceShow reason False else True
   where
      (res, reason) = checkCode a b

有没有更好/更好或更快速的方法来做到这一点?

【问题讨论】:

    标签: haskell quickcheck


    【解决方案1】:

    我假设您的“原因”变量包含某种特定于测试的数据,说明出了什么问题。您可以改为返回一个“结果”,其中包含成功/失败/无效条件和解释问题所在的字符串。返回结果的属性由 QuickCheck 处理,其处理方式与返回 Bool 的属性完全相同。

    (编辑)像这样:

    module QtTest where 
    
    import Test.QuickCheck
    import Test.QuickCheck.Property as P
    
    
    -- Always return success
    prop_one :: Integer -> P.Result
    prop_one _ = MkResult (Just True) True "always succeeds" False [] []
    
    
    -- Always return failure
    prop_two :: Integer -> P.Result
    prop_two n = MkResult (Just False) True ("always fails: n = " ++ show n) False [] []
    

    注意是你想要的Test.QuickCheck.P​​roperty中定义的“Result”类型。

    Test.QuickCheck.P​​roperty 中还定义了一些组合子,它们可以帮助您组合结果而不是直接调用构造函数,例如

    prop_three :: Integer -> Property
    prop_three n = printTestCase ("always fails: n = " ++ show n) False
    

    我想使用这些会更好。

    【讨论】:

    【解决方案2】:

    因为 QuickCheck 为您提供了函数的输入,并且由于被测代码是纯代码(它是,对吗?),您可以将这些输入提供给函数并获得结果。这更加灵活,因为使用这些输入,您还可以通过调整原始函数来重复测试,直到它正确为止。

    【讨论】:

    • 嗯,问题的重点是方便。成功的测试就是尽可能地自动化。必须打开一个 ghci 会话并重新计算一个潜在的昂贵函数是没有意义的。我的意思是,QuickCheck 已经提供了collectclassify 来丰富non-failed 属性测试的输出。这种浓缩是可选的,不会降低灵活性。
    • 我的意思是,这还有一个实际原因:QuickCheck 属性可以任意复杂,因此任何给定测试的“输出”不一定很明显:您需要修改您的代码变成一种形式,上面写着“嘿,这是我感兴趣的中间值!”但是,假设您愿意进行此重写,则没有理由不提供此功能,尽管 IIRC QuickCheck 没有为此目的而内置的东西。
    【解决方案3】:

    这与 Paul Johnson 的答案相同,但对 MkResult 的更改更加简洁和健壮:

    import Test.QuickCheck.Property (succeeded, failed, reason)
    
    prop a b =
      if res /= []
        then succeeded
        else failed { reason = reason }
       where
          (res, reason) = checkCode a b
    

    【讨论】:

    • 谢谢,这非常简单易用。在测试多个属性之前,让我看看当我有一个大的设置时出了什么问题
    【解决方案4】:

    这是我的解决方案(我使用counterexample 而不是printTestCase,因为后者现在已被弃用):

    (<?>) :: (Testable p) => p -> String -> Property
    (<?>) = flip (Test.QuickCheck.counterexample . ("Extra Info: " ++))
    infixl 2 <?>
    

    用法:

    main :: IO ()
    main = hspec $ do
      describe "math" $ do
        prop "sum-of-square-le-square-of-sum" $ do
          \(x :: Int) (y :: Int) ->
            x * x + y * y <= (x + y) * (x + y) <?> show (x * x, y * y, x + y)
    

    所以当一个测试用例失败时,你可以看到类似:

       *** Failed! Falsifiable, Falsifiable (after 2 tests):
       1
       -1
       Extra Info: (1,1,0)
    

    您还可以将&lt;?&gt;.&amp;&amp;..||.=====&gt;等一起使用:

      describe "math" $ do
        prop "sum-of-square-le-square-of-sum" $ do
          \(x :: Int) (y :: Int) ->
            x * x + y * y <= (x + y) * (x + y) <?> show (x * x, y * y, x + y) .||. (1==0) <?> "haha"
    

    【讨论】:

      猜你喜欢
      • 2012-11-22
      • 1970-01-01
      • 1970-01-01
      • 2012-01-22
      • 2019-11-02
      • 2016-08-21
      • 2013-08-05
      • 2019-03-23
      • 1970-01-01
      相关资源
      最近更新 更多