当某个值未能通过 QuickCheck 测试时,我想将其用于调试。有什么办法我可以做类似的事情:
let failValue = quickCheck' myTest
in someStuff failValue
如果我的数据read
能够,那么我可能会通过某种方式从 IO 获取数据,但事实并非如此。
当某个值未能通过 QuickCheck 测试时,我想将其用于调试。有什么办法我可以做类似的事情:
let failValue = quickCheck' myTest
in someStuff failValue
如果我的数据read
能够,那么我可能会通过某种方式从 IO 获取数据,但事实并非如此。
我在 QuickCheck API 中找不到任何可以很好地做到这一点的东西,但这是我使用 monadic QuickCheck API 拼凑起来的东西。它截获并记录到您的属性的输入IORef
,并假设如果失败,则最后一个是罪魁祸首,并将其返回到 a 中Just
。如果测试通过,则结果为Nothing
。这可能可以稍微改进一下,但对于简单的单参数属性,它应该可以完成这项工作。
import Control.Monad
import Data.IORef
import Test.QuickCheck
import Test.QuickCheck.Monadic
prop_failIfZero :: Int -> Bool
prop_failIfZero n = n /= 0
quickCheck' :: (Arbitrary a, Show a) => (a -> Bool) -> IO (Maybe a)
quickCheck' prop = do input <- newIORef Nothing
result <- quickCheckWithResult args (logInput input prop)
case result of
Failure {} -> readIORef input
_ -> return Nothing
where
logInput input prop x = monadicIO $ do run $ writeIORef input (Just x)
assert (prop x)
args = stdArgs { chatty = False }
main = do failed <- quickCheck' prop_failIfZero
case failed of
Just x -> putStrLn $ "The input that failed was: " ++ show x
Nothing -> putStrLn "The test passed"
一种方法是使用sample'方法,手动运行测试并找到它失败的值。例如,测试一个错误的双重功能:
import Test.QuickCheck
double :: Int -> Int
double x | x < 10 = 2 * x
| otherwise = 13
doubleTest :: Int -> Bool
doubleTest x = x + x == double x
tester :: IO ()
tester = do
values <- sample' arbitrary
let failedValues = filter (not . doubleTest) values
print failedValues
唯一的问题是sample'
只生成 11 个测试值,这可能不足以触发 bug。