【发布时间】:2026-01-25 13:30:01
【问题描述】:
当某个值未能通过 QuickCheck 测试时,我想将其用于调试。有什么办法可以做类似的事情:
let failValue = quickCheck' myTest
in someStuff failValue
如果我的数据是readable,那么我可能会破解一些方法从 IO 获取它,但事实并非如此。
【问题讨论】:
标签: haskell quickcheck
当某个值未能通过 QuickCheck 测试时,我想将其用于调试。有什么办法可以做类似的事情:
let failValue = quickCheck' myTest
in someStuff failValue
如果我的数据是readable,那么我可能会破解一些方法从 IO 获取它,但事实并非如此。
【问题讨论】:
标签: haskell quickcheck
我在 QuickCheck API 中找不到任何可以很好地做到这一点的东西,但这是我使用 monadic QuickCheck API 拼凑而成的。它拦截并在IORef 中记录到您的属性的输入,并假设如果失败,则最后一个是罪魁祸首,并将其返回到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"
【讨论】:
whenFail(和wnehFail')(hackage.haskell.org/package/QuickCheck-2.13.2/docs/…)的指针,它需要IO ()在失败时执行,最终成为我使用的。
一种方法是使用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。
【讨论】: