在SML中,如何断言抛出特定异常?

Wan*_*ang 7 testing equality exception sml

在没有花费精力实际克隆JUnit之类的东西的情况下,我正在抛出一些实用程序函数来帮助测试一些SML代码.我确实知道QCheck,但它也不能做到这一点,也不是我想要的.(但是如果您知道另一个SML自动测试框架,请说出来.)

我希望能够断言某些函数会抛出异常,例如,给定一个函数

fun broken x = raise Fail
Run Code Online (Sandbox Code Playgroud)

我希望能够写出类似的东西

throws ("ERROR: function is not broken enough!", fn () => broken 1, Fail)
Run Code Online (Sandbox Code Playgroud)

如果给定的函数没有引发预期的异常,则抛出错误.

我试着throws用这样的类型写一个函数(string * exn * (unit -> unit)) -> unit:

  fun throws (msg, e, func) = func ()
    handle e' => if e = e'
                     then ()
                     else raise ERROR (SOME msg)
Run Code Online (Sandbox Code Playgroud)

但这会产生一堆编译时错误,显然是因为ML没有定义异常上的相等性:

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
Run Code Online (Sandbox Code Playgroud)

作为一种解决方法,我怀疑我可以重用assert我现有的函数:

assert ((broken 1; false) handle Fail => true | _ => false)
Run Code Online (Sandbox Code Playgroud)

但它更多的思考和打字.

那么,有没有办法throws在SML中编写该函数?

Seb*_*olm 8

以下功能应该有效:

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
Run Code Online (Sandbox Code Playgroud)

这使用函数exnName,它将异常的名称作为字符串,并将其用于比较.

更重要的是,它还处理没有抛出任何异常的情况,并且也给出了错误.

或者,如果您只需要一个布尔值,指示是否抛出异常,您可以使用:

fun bthrows (func, e) = (func (); false) handle e' => exnName e = exnName e'
Run Code Online (Sandbox Code Playgroud)

请注意,对于Fail的情况,您实际上必须创建一个Fail-exception的实例,例如:

throws ("ERROR: Oh no!", fn () => test 5, Fail "")
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用例外的名称,以获得更清晰的一般情况:

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'
Run Code Online (Sandbox Code Playgroud)

然后像这样使用它:

throws ("ERROR: Oh no!", fn () => test 5, "Fail")
Run Code Online (Sandbox Code Playgroud)