Without taking the effort to actually clone JUnit or something, I'm throwing together a few utility functions to help test some SML code. I do know about QCheck, but it can't do this one thing either and isn't what I want generally. (But if you know of another automated-testing framework for SML, please speak up.)
I'd like to be able to assert that some function will throw an exception, e.g., given a function
fun broken x = raise Fail
I'd like to be able to write something like
throws ("ERROR: function is not broken enough!", fn () => broken 1, Fail)
and have it throw an error if the given function does not raise the expected exception.
I tried to write a throws
function with type (string * exn * (unit -> unit)) -> unit
like so:
fun throws (msg, e, func) = func ()
handle e' => if e = e'
then ()
else raise ERROR (SOME msg)
But this generates a bunch of compile-time errors, apparently because ML does not define equality over exceptions:
sexp-tests.sml:54.31-57.49 Error: types of rules don't agree [equality type required]
earlier rule(s): ''Z -> unit
this rule: exn -> 'Y
in rule:
exn => raise exn
sexp-tests.sml:54.31-57.49 Error: handler domain is not exn [equality type required]
handler domain: ''Z
in expression:
func ()
handle
e' => if e = e' then () else raise (ERROR <exp>)
| exn => raise exn
As a workaround, I suspect I could just reuse an existing assert
function I have:
assert ((broken 1; false) handle Fail => true | _ => false)
But it's a bit more thinking and typing.
So, is there any way to write that throws
function in SML?
This following function should work:
exception ERROR of string option;
fun throwError msg = raise ERROR (SOME msg);
fun throws (msg, func, e) =
(func (); throwError msg) handle e' =>
if exnName e = exnName e'
then ()
else raise throwError msg
This uses the function exnName, which gets the name of the exception as a string, and uses that for comparison instead.
More importantly, it also handles the case where no exception is thrown at all, and gives an error on that, too.
Alternatively, if you simply need a boolean value, indicating whether the exception was thrown or not, you can use:
fun bthrows (func, e) = (func (); false) handle e' => exnName e = exnName e'
Note that, for the case of Fail, you'll actually have to create an instance of a Fail-exception, for instance like so:
throws ("ERROR: Oh no!", fn () => test 5, Fail "")
Alternatively, you could take the name of the exception, for a cleaner general case:
fun throws (msg, func, e) =
(func (); throwError msg) handle e' =>
if e = exnName e'
then ()
else raise throwError msg
fun bthrows (func, e) = (func (); false) handle e' => e = exnName e'
And then use it like this:
throws ("ERROR: Oh no!", fn () => test 5, "Fail")
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With