How do I test for an error in Haskell?
error-handling, haskell, hunit, testing, unit-testing
Solution
OP's solution defines `assertException`, but it looks like `Test.HUnit.Tools.assertRaises` from testpack is also usable here.
I added the `msg` argument to `assertError` to match how `assertRaises` works, and included selective imports so noobs like me can learn where commonly used things are imported from.
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
Problem
I want to be able to make sure a function will throw an error when it receives and invalid value. For example, let says I have a function pos that only returns a positive number: ``` pos :: Int -> Int pos x | x >= 0 = x | otherwise = error "Invalid Input" ``` This is a simplistic example, but I hope you get the idea. I want to be able to write a test case that will expect an error and consider it a passing test. For example: ``` tests = [pos 1 == 1, assertError pos (-1), pos 2 == 2, assertError pos (-2)] runTests = all (== True) tests ``` [My Solution] This is what I ended up going with based on @hammar's comment. ``` 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 ```