HUnit - testing for failed pattern match

Hello, Given the following contrived function: giveMeThree x | x == 3 = True How do I test with HUnit that 2 is invalid input? giveMeThreeTests = [ "Likes 3" ~: True ~=? giveMeThree 3 , "Dislikes 2" ~: ..... -- what? ] I couldn't find anything mentioned in the HUnit user guide. References to appropriate documentation appreciated. Cheers, Xavier

Hello Xavier A partial function will emit an exception, which must be caught from IO. I'm sure HUnit has an appropriate mechanism for handling this, but I strongly suggest you redesign your function to be total: instead of omitting pattern matches, change it to use Maybe and return Nothing in the 'otherwise' case. Cheers, Edward Excerpts from Xavier Shay's message of Tue Feb 22 05:59:12 -0500 2011:
Hello,
Given the following contrived function:
giveMeThree x | x == 3 = True
How do I test with HUnit that 2 is invalid input?
giveMeThreeTests = [ "Likes 3" ~: True ~=? giveMeThree 3 , "Dislikes 2" ~: ..... -- what? ]
I couldn't find anything mentioned in the HUnit user guide. References to appropriate documentation appreciated.
Cheers, Xavier

Excerpts from Xavier Shay's message of Tue Feb 22 05:59:12 -0500 2011:
Given the following contrived function:
giveMeThree x | x == 3 = True
How do I test with HUnit that 2 is invalid input?
giveMeThreeTests = [ "Likes 3" ~: True ~=? giveMeThree 3 , "Dislikes 2" ~: ..... -- what? ]
I couldn't find anything mentioned in the HUnit user guide. References to appropriate documentation appreciated.
On 23/02/11 1:31 AM, Edward Z. Yang wrote:
A partial function will emit an exception, which must be caught from IO. I'm sure HUnit has an appropriate mechanism for handling this, but I strongly suggest you redesign your function to be total: instead of omitting pattern matches, change it to use Maybe and return Nothing in the 'otherwise' case.
In this case, the inputs I am giving to the function are totally invalid - they should never occur in a running app. For context, the function takes a state, applies a transition, and returns the new state. There are certain invalid transitions. In other languages I would throw an ArgumentError or similar ... would the Haskell way be to return Nothing instead? To me this seems to be complicating my types (Maybe instead of a vanilla type) for a benefit that I don't see. What are the benefits to using Maybe in this case rather than throwing an exception? Cheers, Xavier

What you describe is one of the cases when a partial function is ok (though it's probably better to do something like: giveMeThree x | x == 3 = True | otherwise = error "giveMeThree: not three" It's frequently not possible, but if you can arrange your types so that the invalid values are not possible, even better.) Cheers, Edward

On 23/02/11 10:35 AM, Edward Z. Yang wrote:
What you describe is one of the cases when a partial function is ok (though it's probably better to do something like:
giveMeThree x | x == 3 = True | otherwise = error "giveMeThree: not three"
It's frequently not possible, but if you can arrange your types so that the invalid values are not possible, even better.) ah that's good, I can give a helpful error message.
Still would like a way to test it though...

Excerpts from Xavier Shay's message of Tue Feb 22 20:25:28 -0500 2011:
On 23/02/11 10:35 AM, Edward Z. Yang wrote:
What you describe is one of the cases when a partial function is ok (though it's probably better to do something like:
giveMeThree x | x == 3 = True | otherwise = error "giveMeThree: not three"
It's frequently not possible, but if you can arrange your types so that the invalid values are not possible, even better.) ah that's good, I can give a helpful error message.
Still would like a way to test it though...
Check the section "Testing for expected errors" herE: http://leiffrenzel.de/papers/getting-started-with-hunit.html Edward

On 24/02/11 12:31 AM, Edward Z. Yang wrote:
Excerpts from Xavier Shay's message of Tue Feb 22 20:25:28 -0500 2011:
On 23/02/11 10:35 AM, Edward Z. Yang wrote:
What you describe is one of the cases when a partial function is ok (though it's probably better to do something like:
giveMeThree x | x == 3 = True | otherwise = error "giveMeThree: not three"
It's frequently not possible, but if you can arrange your types so that the invalid values are not possible, even better.) ah that's good, I can give a helpful error message.
Still would like a way to test it though...
Check the section "Testing for expected errors" herE:
http://leiffrenzel.de/papers/getting-started-with-hunit.html Excellent. I had to update the code to use the new Control.Exception (rather than Control.OldException), and ended up with:
import Control.Exception TestCase $ do handle (\(_ :: ErrorCall) -> return ()) $ do evaluate ( myFunc 3 [False, False, True] ) assertFailure "must raise an error on invalid state" The only issue is that I now require the -XScopedTypeVariables flag, otherwise it fails to compile with: Illegal signature in pattern: ErrorCall Use -XScopedTypeVariables to permit it I am not sure whether this is an acceptable solution or not. I tried reading this: http://www.haskell.org/ghc/docs/latest/html/users_guide/other-type-extension... ... but I don't know enough about haskell for it to make any sense to me yet. Cheers, Xavier

On Saturday 26 February 2011 02:08:48, Xavier Shay wrote:
Excellent. I had to update the code to use the new Control.Exception (rather than Control.OldException), and ended up with:
import Control.Exception
TestCase $ do handle (\(_ :: ErrorCall) -> return ()) $ do evaluate ( myFunc 3 [False, False, True] ) assertFailure "must raise an error on invalid state"
The only issue is that I now require the -XScopedTypeVariables flag, otherwise it fails to compile with:
Illegal signature in pattern: ErrorCall Use -XScopedTypeVariables to permit it
I am not sure whether this is an acceptable solution or not.
ScopedTypeVariables is harmless, so it is accetable. But if you prefer, you can avoid it with a top-level handler ignoreErrorCalls :: ErrorCall -> IO () ignoreErrorCalls _ = return () TestCase $ ignoreErrorCalls $ do evaluate ( myFunc 3 [False, False, True] ) assertFailure "must raise an error on invalid state" or an explicit pattern in the handler, TestCase $ handle (\(ErrorCall _) -> return ()) $ do evaluate ... I think (code untested).
I tried reading this: http://www.haskell.org/ghc/docs/latest/html/users_guide/other-type-exten sions.html#pattern-type-sigs
... but I don't know enough about haskell for it to make any sense to me yet.
Cheers, Xavier

On 26/02/11 12:36 PM, Daniel Fischer wrote:
On Saturday 26 February 2011 02:08:48, Xavier Shay wrote:
Excellent. I had to update the code to use the new Control.Exception (rather than Control.OldException), and ended up with:
import Control.Exception
TestCase $ do handle (\(_ :: ErrorCall) -> return ()) $ do evaluate ( myFunc 3 [False, False, True] ) assertFailure "must raise an error on invalid state"
The only issue is that I now require the -XScopedTypeVariables flag, otherwise it fails to compile with:
Illegal signature in pattern: ErrorCall Use -XScopedTypeVariables to permit it
I am not sure whether this is an acceptable solution or not.
ScopedTypeVariables is harmless, so it is accetable. But if you prefer, you can avoid it with a top-level handler
ignoreErrorCalls :: ErrorCall -> IO () ignoreErrorCalls _ = return ()
TestCase $ ignoreErrorCalls $ do evaluate ( myFunc 3 [False, False, True] ) assertFailure "must raise an error on invalid state"
or an explicit pattern in the handler,
TestCase $ handle (\(ErrorCall _) -> return ()) $ do evaluate ...
I think (code untested). tested the latter - totally works
case closed! Thanks, Xavier
participants (3)
-
Daniel Fischer
-
Edward Z. Yang
-
Xavier Shay