在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
在库中具有后一种类型?
答案 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
现在我们可以进行测试,看看assert
和import 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
stop
和prop_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
可分别由succeeded
和failed
替换。这只是为了表明现在我们不仅限于True
,而是我们可以使用任何False
。
输出是:
prop_assert:
***失败了!断言失败(1次测试后):
prop_stop:
+++好的,通过了100次测试。
我们可以看到,尽管第一个Bool
成功,Testable
由于第二个assertIO
而失败。另一方面,prop_assert
通过了测试,因为第一个assertIO
成功,计算在此时停止,而不是测试第二个prop_stop
。