Based on SO question 13350164 How do I test for an error in Haskell?, I'm trying to write a unit test which asserts that given invalid input, a recursive function raises an exception. The approach I adopted works well for non-recursive functions (or when the first call raises the exception), but as soon as the exception occurs deeper in the call chain, the assertion fails.
I've read the excellent answers to question 6537766 Haskell approaches to error handling but unfortunately the advice is a bit too generic for this point of my learning curve. My guess is that the problem here is connected to lazy evaluation and non-pure testing code, but I'd appreciate an expert explanation.
Should I take a different approach to error handling in situations like this (e.g. Maybe
or Either
), or is there a reasonable fix for making the test case work correctly while using this style?
Here's the code I've come up with. The first two test cases succeed, but the third one fails with "Received no exception, but was expecting exception: Negative item"
.
import Control.Exception (ErrorCall(ErrorCall), evaluate)
import Test.HUnit.Base ((~?=), Test(TestCase, TestList))
import Test.HUnit.Text (runTestTT)
import Test.HUnit.Tools (assertRaises)
sumPositiveInts :: [Int] -> Int
sumPositiveInts [] = error "Empty list"
sumPositiveInts (x:[]) = x
sumPositiveInts (x:xs) | x >= 0 = x + sumPositiveInts xs
| otherwise = error "Negative item"
instance Eq ErrorCall where
x == y = (show x) == (show y)
assertError msg ex f =
TestCase $ assertRaises msg (ErrorCall ex) $ evaluate f
tests = TestList [
assertError "Empty" "Empty list" (sumPositiveInts ([]))
, assertError "Negative head" "Negative item" (sumPositiveInts ([-1, -1]))
, assertError "Negative second item" "Negative item" (sumPositiveInts ([1, -1]))
]
main = runTestTT tests