10

我正在尝试在 F# 中编写一些 NUnit 测试,并且无法将函数传递给ThrowsConstraint. 蒸馏(非)工作样品如下。

open System.IO
open NUnit.Framework

[<TestFixture>]
module Example =

    [<Test>]
    let foo() = 
        let f = fun () -> File.GetAttributes("non-existing.file")
        Assert.That(f, Throws.TypeOf<FileNotFoundException>())

这编译得很好,但我从 NUnit 测试运行程序中得到以下信息:

FsTest.Tests.Example.foo:
System.ArgumentException : The actual value must be a TestDelegate but was f@11
Parameter name: actual

虽然我可以使用ExpectedException属性解决问题,但我的问题是在这种情况下使用 F# 函数的正确方法是什么?

4

2 回答 2

10

为了使您的原始代码段正常工作,您需要做的就是修复签名f符合. 只需丢弃返回值:TestDelegateunit -> unitFile.GetAttributes

let f = fun () -> File.GetAttributes("non-existing.file") |> ignore

F# 编译器没有对您的原始代码感到厌烦,因为只Assert.That(actual: obj, expression: Constraints.IResolveConstraint)存在另一个合适的 NUnit 重载。

由于Assert.That具有非常广泛的用途,我会坚持测试更具体的断言形式的预期异常,例如:

[<Test>]
let foo() =
    Assert.Throws<FileNotFoundException>
        (fun () -> File.GetAttributes("non-existing.file")|> ignore)
    |> ignore

F# 编译器将能够静态发现函数的错误签名。

于 2012-12-12T05:01:48.310 回答
4

恕我直言,您可以通过在 NUnit 之上使用Unquote来节省一些痛苦。那么你的断言就像

[<Test>]
let foo() = 
    raises<FileNotFoundException> <@ File.GetAttributes("non-existing.file") @>

NUnit 的大型断言重载套件有时会出现意外的运行时行为,旨在弥补 C# 与 F# 相比相对缺乏表现力。

另一方面,由于 F# 已经配备了结构比较等功能,可以优雅地表达断言,Unquote 旨在利用其原生功能,仅使用三个简单的断言运算符:testraisesraisesWith.

于 2012-12-12T14:15:42.577 回答