试图组合一些断言函数,但我无法尝试工作

Trying to put together a few assertion functions and i cannot get a try with to work

我只是在学习 F#,所以我正在尝试一些东西(我知道可以只使用 XUnit 或其他东西)

我有下面的断言方法,想法是它应该接受一个预期的异常和它期望抛出这个异常的函数,然后如果抛出的异常是和预期的一样。

let assertException (testName : string) (expected : 'a when 'a :> Exception) functionToBeTested =
    try
        functionToBeTested
        (false)
    with
    | :? Exception as someException when someException :? expected ->
            printTestResultInMiddle (sprintf "Test: %s PASSED: Raised expected exception %A" testName expected) true 
            (true)
    | _ ->
        printTestResultInMiddle (sprintf "Test: %s FAILED: expected exception %A" testName expected) false
        (false)

它在我尝试调用打印方法的行中给出了错误 Unexpected symbol '(' in pattern matching. Expected '->' or other token.。我不应该把这个 try ... with 当作

match ... with

??

还有一个问题,我可以更轻松地做到这一点吗?

显然您需要在此处将 when 表达式括起来。您需要检查 expected 类型 ,即 'a。以下应该编译(当我用 printfn 调用替换你的 printTestResultInMiddle 调用时它对我有用):

let assertException (testName : string) (expected : 'a when 'a :> Exception) functionToBeTested =
    try
        functionToBeTested
        (false)
    with
    | :? Exception as someException when (someException :? 'a) ->
            printTestResultInMiddle (sprintf "Test: %s PASSED: Raised expected exception %A" testName expected) true 
            (true)
    | _ ->
        printTestResultInMiddle (sprintf "Test: %s FAILED: expected exception %A" testName expected) false
        (false)

但是,这会在 :? Exception as someException 表达式上发出警告:

warning FS0067: This type test or downcast will always hold

那是因为根据 try ... with documentation,裸标识符等同于 :? System.Exception as <identifier>。因此,您可以将函数简化为:

let assertException (testName : string) (expected : 'a when 'a :> Exception) functionToBeTested =
    try
        functionToBeTested
        (false)
    with
    | someException when (someException :? 'a) ->
            printTestResultInMiddle (sprintf "Test: %s PASSED: Raised expected exception %A" testName expected) true 
            (true)
    | _ ->
        printTestResultInMiddle (sprintf "Test: %s FAILED: expected exception %A" testName expected) false
        (false)

但实际上,这样做更简单:

let assertException (testName : string) (expected : 'a when 'a :> Exception) functionToBeTested =
    try
        functionToBeTested
        (false)
    with
    | :? 'a ->
            printTestResultInMiddle (sprintf "Test: %s PASSED: Raised expected exception %A" testName expected) true 
            (true)
    | _ ->
        printTestResultInMiddle (sprintf "Test: %s FAILED: expected exception %A" testName expected) false
        (false)

当我在 F# Interactive 中尝试它时,它也会编译,尽管我还没有实际测试它。我确实注意到 try 表达式中的 functionToBeTested 可能应该是函数调用(即 functionToBeTested ()。而且您不需要 (true)(false) 表达式任.

所以你的代码再迭代一次,尽可能简单而不改变它的语义(注意我已经稍微改变了它的语义 functionToBeTested ()) 将是:

let assertException (testName : string) (expected : 'a when 'a :> Exception) functionToBeTested =
    try
        functionToBeTested ()
        false
    with
    | :? 'a ->
            printTestResultInMiddle (sprintf "Test: %s PASSED: Raised expected exception %A" testName expected) true 
            true
    | _ ->
        printTestResultInMiddle (sprintf "Test: %s FAILED: expected exception %A" testName expected) false
        false

第一,
您正在尝试将类型查询运算符 :? 与类型 'a 的值 expected 一起使用。此运算符不能用于值,只能用于类型:

let x = box 5
let a = x :? int    // true
let b = x :? string // false

let y = 10
let c = x :? y  // error: type 'y' is not defined

在您的示例中(使用异常),它看起来像这样:

someException :? InvalidOperationException

或者,如果要与类型参数进行比较:

someException :? 'a

第二,
如果你只想比较它的类型,为什么还要给 someException 命名?这正是 with | :? 子句开头所做的:

try
    ...
with
| :? 'a ->
   ...

然后,您实际上不需要值 expected,因为您想要探查的只是类型。所以你可以只声明通用参数并取消常规参数:

let assertException<'a> (testName : string) functionToBeTested =
   ...

最后,
你的 functionToBeTested 实际上不是一个函数,因为你没有调用它。如果你想验证它在执行过程中是否抛出特定的异常,你需要实际调用:

        functionToBeTested()

综合起来:

let assertException<'a when :> exn> (testName : string) functionToBeTested =
    try
        functionToBeTested()
        (false)
    with
    | :? 'a ->
            printTestResultInMiddle (sprintf "Test: %s PASSED: Raised expected exception %A" testName typeof<'a>.Name) true 
            (true)
    | _ ->
        printTestResultInMiddle (sprintf "Test: %s FAILED: expected exception %A" testName typeof<'a>.Name) false
        (false)