當一個值未通過QuickCheck'd測試時,我想用它進行調試。有什麼辦法,我可以這樣做:找到失敗的值進行快速檢查
let failValue = quickCheck' myTest
in someStuff failValue
如果我的數據是read
能夠那麼我或許可以破解一些方法來從IO得到它,但它不是。
當一個值未通過QuickCheck'd測試時,我想用它進行調試。有什麼辦法,我可以這樣做:找到失敗的值進行快速檢查
let failValue = quickCheck' myTest
in someStuff failValue
如果我的數據是read
能夠那麼我或許可以破解一些方法來從IO得到它,但它不是。
我在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"
一種方法是使用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個測試值,即可能不足以觸發錯誤。
非常聰明,謝謝 – Xodarap
這個小竅門讓我的Haskell調試體驗好了很多。謝謝 –