В SML, как утверждать, что выброшено определенное исключение? - PullRequest
7 голосов
/ 13 мая 2011

Не прилагая усилий для фактического клонирования JUnit или чего-то еще, я собираю вместе несколько служебных функций, чтобы помочь протестировать некоторый код SML.Я действительно знаю о QCheck, но он тоже не может сделать это, и это не то, что я хочу в общем.(Но если вам известна другая автоматизированная среда тестирования для SML, пожалуйста, скажите об этом.)

Я бы хотел утверждать, что какая-то функция вызовет исключение, например, с учетом функции

fun broken x = raise Fail

Я хотел бы иметь возможность написать что-то вроде

throws ("ERROR: function is not broken enough!", fn () => broken 1, Fail)

и заставить его выдать ошибку, если данная функция не вызовет ожидаемое исключение.

Я пытался написать throws функцию с типом (string * exn * (unit -> unit)) -> unit, например, так:

  fun throws (msg, e, func) = func ()
    handle e' => if e = e'
                     then ()
                     else raise ERROR (SOME msg)

Но это порождает кучу ошибок времени компиляции, по-видимому, потому что 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

В качестве обходного пути, я подозреваю, что я мог бы просто повторно использовать существующую assert функцию, которую я имею:

assert ((broken 1; false) handle Fail => true | _ => false)

Но это немного больше думать и печатать.

Итак, есть ли способ написать эту throws функцию в SML?

1 Ответ

8 голосов
/ 13 мая 2011

Эта следующая функция должна работать:

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

При этом используется функция exnName , которая получает имя исключения в виде строки и использует его для сравнения.

Что еще более важно, он также обрабатывает случай, когда исключение не выдается вообще, и также выдает ошибку по этому поводу.

В качестве альтернативы, если вам просто нужно логическое значение, указывающее, было ли сгенерировано исключение, вы можете использовать:

fun bthrows (func, e) = (func (); false) handle e' => exnName e = exnName e'

Обратите внимание, что для случая Fail вам фактически необходимо создать экземпляр исключения Fail, например, так:

throws ("ERROR: Oh no!", fn () => test 5, Fail "")

В качестве альтернативы, вы можете взять имя исключения, для более чистого общего случая:

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'

А затем используйте это так:

throws ("ERROR: Oh no!", fn () => test 5, "Fail")
...