如何在Haskell中测试错误?

Joe*_*and 8 testing error-handling haskell unit-testing hunit

我希望能够确保函数在接收到无效值时会抛出错误.例如,假设我有一个只返回正数的函数pos:

pos :: Int -> Int
pos x
   | x >= 0 = x
   | otherwise = error "Invalid Input"
Run Code Online (Sandbox Code Playgroud)

这是一个简单的例子,但我希望你能得到这个想法.

我希望能够编写一个期望出现错误的测试用例并将其视为通过测试.例如:

tests = [pos 1 == 1, assertError pos (-1), pos 2 == 2, assertError pos (-2)]
runTests = all (== True) tests
Run Code Online (Sandbox Code Playgroud)

[我的解决方案]

这是我最终根据@ hammar的评论进行的.

instance Eq ErrorCall where
    x == y = (show x) == (show y)

assertException :: (Exception e, Eq e) => e -> IO a -> IO ()
assertException ex action =
    handleJust isWanted (const $ return ()) $ do
        action
        assertFailure $ "Expected exception: " ++ show ex
  where isWanted = guard . (== ex) 

assertError ex f = 
    TestCase $ assertException (ErrorCall ex) $ evaluate f

tests = TestList [ (pos 0) ~?= 0
                 , (pos 1) ~?= 1
                 , assertError "Invalid Input" (pos (-1))
                 ]   

main = runTestTT tests
Run Code Online (Sandbox Code Playgroud)

aka*_*ola 4

OP 的解决方案定义了assertException,但看起来Test.HUnit.Tools.assertRaises来自testpack 的也可以在这里使用。

我添加了msg参数来assertError匹配assertRaises工作方式,并包括选择性导入,这样像我这样的菜鸟就可以了解常用的东西是从哪里导入的。

import Control.Exception (ErrorCall(ErrorCall), evaluate)
import Test.HUnit.Base  ((~?=), Test(TestCase, TestList))
import Test.HUnit.Text (runTestTT)
import Test.HUnit.Tools (assertRaises)

pos :: Int -> Int
pos x
   | x >= 0 = x
   | otherwise = error "Invalid Input"

instance Eq ErrorCall where
    x == y = (show x) == (show y)

assertError msg ex f = 
    TestCase $ assertRaises msg (ErrorCall ex) $ evaluate f

tests = TestList [
  (pos 0) ~?= 0
  , (pos 1) ~?= 1
  , assertError "Negative argument raises an error" "Invalid Input" (pos (-1))
  ]   

main = runTestTT tests
Run Code Online (Sandbox Code Playgroud)