快速检查运行时错误
Quickcheck for runtime errors
我有兴趣使用快速检查库,但它似乎是为测试属性而设计的。我想做的是为我定义的数据类型和我编写的测试函数生成随机数据。我不关心结果是什么,只关心函数在输入随机数据时是否产生运行时错误。我看到的所有快速检查示例都是用于测试函数的属性,例如在输入随机数据时结果是否大于 5。有没有办法以这种方式使用快速检查?像
data Result = A | B
fun :: Result -> Int
fun A = 5
main = check fun
在上面的代码中,我有一个自定义数据类型和一个不完整的函数。如果通过 B,此函数将失败。当然,运行时错误的类型不仅仅是不完整的函数。我想检查生成数据,并将其提供给函数。不关心结果如何。快速检查可以做到这一点吗?
编辑 -
我应该注意,我不是在寻找检查不完整模式的标志,什么不是。我对通用运行时错误检查很感兴趣。
而是尽可能确保不处理 IO
的函数不抛出异常。
异常只能在 IO
中捕获,并且它们会破坏您原本期望从纯函数中获得的东西。
有一些来自
Control.Exception
和 Test.QuickCheck.Monadic
:
> import Control.Exception (Exception, PatternMatchFail,
> SomeException, evaluate,
> fromException, try)
> import Data.Either (either)
> import Test.Hspec (anyException, describe, hspec, it,
> shouldThrow)
> import Test.QuickCheck hiding (Result)
> import Test.QuickCheck.Monadic (assert, monadicIO, run)
对于初学者,让我们编写一个函数,使我们能够检查是否抛出了某个异常:
> throwsExceptionOr :: Exception e => (e -> Bool) -> (a -> Bool) -> a -> IO Bool
> throwsExceptionOr pe pa = fmap (either pe pa) . try . evaluate
这使您能够像这样编写测试:
> prop_fun_1 x = monadicIO . run $
> throwsExceptionOr (const True :: SomeException -> Bool)
> (== 5)
> (foo x)
throwsExceptionOr
很通用,所以你可以定义自己的助手:
> -- | Should always throw an exception.
> throwsException :: Exception e => (e -> Bool) -> a -> IO Bool
> throwsException p = fmap (either p (const False)) . try . evaluate
> -- | Should either pass the test or throw an exception.
> exceptionOr :: a -> (a -> Bool) -> IO Bool
> exceptionOr x p = fmap (either anyException p) . try . evaluate $ x
> where
> anyException :: SomeException -> Bool
> anyException = const True
现在您可以像往常一样编写测试了:
> data Result = A | B deriving (Enum, Show, Eq, Ord, Bounded, Read)
>
> instance Arbitrary Result where
> arbitrary = elements [A, B]
>
> foo :: Result -> Int
> foo A = 5
>
> prop_foo x = monadicIO . run $ foo x `exceptionOr` (== 5)
您可以更进一步,将 monadicIO . run
移动到另一个助手中,但是
留作练习。此外,您可以使功能兼容
与其他测试框架,例如 hspec
或 tasty
:
> main :: IO ()
> main = hspec $ do
> describe "foo" $ do
> it "either returns five or throws a pattern match fail" $ propertyIO $ \x ->
> throwsExceptionOr patternMatchFail (==5) (foo x)
>
> it "throws an exception on input B" $
> evaluate (foo B) `shouldThrow` anyException
>
> where
> patternMatchFail :: PatternMatchFail -> Bool
> patternMatchFail _ = True
>
> -- I think there is a better combinator for this
> propertyIO f = property $ \x -> monadicIO . run $ f x
话虽如此,无论使用何种语言你都想摆脱
如果可能,编译时可能出现的运行时错误。这包括获得
摆脱部分功能或可能的类型废话。这个要看实际
使用,当然。如果您可以验证 head
always 在非空的情况下被调用
列出整个程序,继续使用它。如果不能,请使用模式
匹配(参见 this discussion
在 head
的类型上)。
无论哪种方式,鉴于旧版本的 GHC 不提供堆栈调用,您宁愿在编译时出现错误,也不愿在运行时出现没有堆栈跟踪的错误(最新版本的 GHC 对此有一些不错的功能) .
我有兴趣使用快速检查库,但它似乎是为测试属性而设计的。我想做的是为我定义的数据类型和我编写的测试函数生成随机数据。我不关心结果是什么,只关心函数在输入随机数据时是否产生运行时错误。我看到的所有快速检查示例都是用于测试函数的属性,例如在输入随机数据时结果是否大于 5。有没有办法以这种方式使用快速检查?像
data Result = A | B
fun :: Result -> Int
fun A = 5
main = check fun
在上面的代码中,我有一个自定义数据类型和一个不完整的函数。如果通过 B,此函数将失败。当然,运行时错误的类型不仅仅是不完整的函数。我想检查生成数据,并将其提供给函数。不关心结果如何。快速检查可以做到这一点吗?
编辑 - 我应该注意,我不是在寻找检查不完整模式的标志,什么不是。我对通用运行时错误检查很感兴趣。
而是尽可能确保不处理 IO
的函数不抛出异常。
异常只能在 IO
中捕获,并且它们会破坏您原本期望从纯函数中获得的东西。
有一些来自
Control.Exception
和 Test.QuickCheck.Monadic
:
> import Control.Exception (Exception, PatternMatchFail,
> SomeException, evaluate,
> fromException, try)
> import Data.Either (either)
> import Test.Hspec (anyException, describe, hspec, it,
> shouldThrow)
> import Test.QuickCheck hiding (Result)
> import Test.QuickCheck.Monadic (assert, monadicIO, run)
对于初学者,让我们编写一个函数,使我们能够检查是否抛出了某个异常:
> throwsExceptionOr :: Exception e => (e -> Bool) -> (a -> Bool) -> a -> IO Bool
> throwsExceptionOr pe pa = fmap (either pe pa) . try . evaluate
这使您能够像这样编写测试:
> prop_fun_1 x = monadicIO . run $
> throwsExceptionOr (const True :: SomeException -> Bool)
> (== 5)
> (foo x)
throwsExceptionOr
很通用,所以你可以定义自己的助手:
> -- | Should always throw an exception.
> throwsException :: Exception e => (e -> Bool) -> a -> IO Bool
> throwsException p = fmap (either p (const False)) . try . evaluate
> -- | Should either pass the test or throw an exception.
> exceptionOr :: a -> (a -> Bool) -> IO Bool
> exceptionOr x p = fmap (either anyException p) . try . evaluate $ x
> where
> anyException :: SomeException -> Bool
> anyException = const True
现在您可以像往常一样编写测试了:
> data Result = A | B deriving (Enum, Show, Eq, Ord, Bounded, Read)
>
> instance Arbitrary Result where
> arbitrary = elements [A, B]
>
> foo :: Result -> Int
> foo A = 5
>
> prop_foo x = monadicIO . run $ foo x `exceptionOr` (== 5)
您可以更进一步,将 monadicIO . run
移动到另一个助手中,但是
留作练习。此外,您可以使功能兼容
与其他测试框架,例如 hspec
或 tasty
:
> main :: IO ()
> main = hspec $ do
> describe "foo" $ do
> it "either returns five or throws a pattern match fail" $ propertyIO $ \x ->
> throwsExceptionOr patternMatchFail (==5) (foo x)
>
> it "throws an exception on input B" $
> evaluate (foo B) `shouldThrow` anyException
>
> where
> patternMatchFail :: PatternMatchFail -> Bool
> patternMatchFail _ = True
>
> -- I think there is a better combinator for this
> propertyIO f = property $ \x -> monadicIO . run $ f x
话虽如此,无论使用何种语言你都想摆脱
如果可能,编译时可能出现的运行时错误。这包括获得
摆脱部分功能或可能的类型废话。这个要看实际
使用,当然。如果您可以验证 head
always 在非空的情况下被调用
列出整个程序,继续使用它。如果不能,请使用模式
匹配(参见 this discussion
在 head
的类型上)。
无论哪种方式,鉴于旧版本的 GHC 不提供堆栈调用,您宁愿在编译时出现错误,也不愿在运行时出现没有堆栈跟踪的错误(最新版本的 GHC 对此有一些不错的功能) .