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 ```

Original source

Related problems