Test.QuickCheck.Monadic:为什么断言应用于Bool,而不是Testable a =>一个

时间:2015-07-29 13:29:08

标签: haskell quickcheck

Testing Monadic Code with QuickCheck (Claessen, Hughes 2002)中,assert的类型为:

assert :: (Monad m, Testable a) => a -> PropertyM m ()

但是,在Test.QuickCheck.Monadic中,它的类型为:

assert :: (Monad m) => Bool -> PropertyM m ()

为什么assert在库中具有后一种类型?

1 个答案:

答案 0 :(得分:4)

我认为这是由于技术限制,因为目前要使用Testable库评估Test.QuickCheck,您需要使用其中一个quickCheck*函数,这些函数非常counterexample 1}} - 中心的。这是因为QuickCheck通过随机生成可能的输入(默认为100)来测试IO属性,试图找到证明属性为false的source code。如果未找到此类输入,则假定该属性为真(虽然这不一定是事实;可能存在未经测试的反例)。为了能够在Haskell中生成随机输入,我们坚持使用Testable monad。

请注意,尽管IO是以这种通用方式定义的,但它仅用于assert的所有论文。因此,图书馆作者(与论文相同)倾向于牺牲简单Bool的通用Testable参数,此时不强制任何monad。

我们可以看到他们甚至在https://github.com/REMitchell/python-scraping/blob/master/chapter14/1-socks.py中将原始签名写成评论:

Bool

另请注意,尽管-- assert :: Testable prop => prop -> PropertyM m () 函数具有类似的签名:

stop

与论文中的stop :: (Testable prop, Monad m) => prop -> PropertyM m a 函数相同,因为前者在两种情况下停止计算条件为{{ 1}}或assert。另一方面,如果条件为True:{/ p>,False将仅停止计算

  

⟦断言True»p⟧=⟦p⟧

     

⟦断言False»p⟧= {return False}

我们可以轻松地从论文中写出assert版本的False函数:

IO

现在我们可以进行测试,看看assertimport Control.Monad import Control.Monad.Trans import Test.QuickCheck import Test.QuickCheck.Monadic import Test.QuickCheck.Property import Test.QuickCheck.Test assertIO :: Testable prop => prop -> PropertyM IO () assertIO p = do r <- liftIO $ quickCheckWithResult stdArgs{chatty = False} p unless (isSuccess r) $ fail "Assertion failed" 之间的差异:

assertIO

stopprop_assert :: Property prop_assert = monadicIO $ do assertIO succeeded assertIO failed prop_stop :: Property prop_stop = monadicIO $ do stop succeeded stop failed main :: IO () main = do putStrLn "prop_assert:" quickCheck prop_assert putStrLn "prop_stop:" quickCheck prop_stop 可分别由succeededfailed替换。这只是为了表明现在我们不仅限于True,而是我们可以使用任何False

输出是:

  

prop_assert:
  ***失败了!断言失败(1次测试后):
  prop_stop:
  +++好的,通过了100次测试。

我们可以看到,尽管第一个Bool成功,Testable由于第二个assertIO而失败。另一方面,prop_assert通过了测试,因为第一个assertIO成功,计算在此时停止,而不是测试第二个prop_stop