我已经看到如何使用 QuickCheck 测试单子和非单子代码,但是如何使用它来测试处理错误的代码,即打印一些消息然后调用exitWith
?
问问题
1008 次
2 回答
4
先声明一下:我不是 QuickCheck 方面的专家,在你提出问题之前我没有单子检查的经验,但我认为 stackoverflow 是学习新事物的机会。如果有专家回答说这可以做得更好,我会删除我的。
假设您有一个test
可以使用exitWith
. 这就是我认为你可以测试它的方法。关键功能是protect
,它捕获异常并将其转换为您可以测试的东西。
import System.Exit
import Test.QuickCheck
import Test.QuickCheck.Property
import Test.QuickCheck.Monadic
test :: Int -> IO Int
test n | n > 100 = do exitWith $ ExitFailure 1
| otherwise = do print n
return n
purifyException :: (a -> IO b) -> a -> IO (Maybe b)
purifyException f x = protect (const Nothing) $ return . Just =<< f x
testProp :: Property
testProp = monadicIO $ do
input <- pick arbitrary
result <- run $ purifyException test $ input
assert $ if input <= 100 then result == Just input
else result == Nothing
据我所知,这有两个缺点,但我没有办法克服它们。
我发现无法
ExitCode
从AnException
可以protect
处理的异常中提取异常。因此,所有退出代码在这里都被视为相同(它们被映射到Nothing
)。我本来希望有:purifyException :: (a -> IO b) -> a -> IO (Either a ExitCode)
我发现无法测试测试的 I/O 行为。假设
test
是:test :: IO () test = do n <- readLn if n > 100 then exitWith $ ExitFailure 1 else print n
那你会怎么测试呢?
我也会感谢更多专家的答案。
于 2013-09-01T21:20:49.607 回答
3
QuickCheckexpectFailure
功能可用于处理此类事情。采用这个简单(不推荐)的错误处理框架:
import System.Exit
import Test.QuickCheck
import Test.QuickCheck.Monadic
handle :: Either a b -> IO b
handle (Left _) = putStrLn "exception!" >> exitWith (ExitFailure 1)
handle (Right x) = return x
并启动几个虚拟函数:
positive :: Int -> Either String Int
positive x | x > 0 = Right x
| otherwise = Left "not positive"
negative :: Int -> Either String Int
negative x | x < 0 = Right x
| otherwise = Left "not negative"
现在我们可以测试错误处理的一些属性。首先,Right
值不应导致异常:
prop_returnsHandledProperly (Positive x) = monadicIO $ do
noErr <- run $ handle (positive x)
assert $ noErr == x
-- Main*> quickCheck prop_returnsHandledProperly
-- +++ OK, passed 100 tests.
Lefts
应该导致异常。请注意expectFailure
附加到开头:
prop_handlesExitProperly (Positive x) = expectFailure . monadicIO $
run $ handle (negative x)
-- Main*> quickCheck prop_handlesExitProperly
-- +++ OK, failed as expected. Exception: 'exitWith: invalid argument (ExitFailure 0)' (after 1 test):
于 2013-09-01T21:37:33.857 回答