Serge Belov
Serge Belov

Reputation: 5803

How do I pass a function to NUnit Throws Constraint?

I'm trying to write some NUnit tests in F# and having trouble passing a function to the ThrowsConstraint. A distilled (non)working sample is below.

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>())

This compiles just fine but I get the following from the NUnit test runner:

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

While I'm able to work around the problem using ExpectedException attribute, my question is what is the correct way of using an F# function in this situation?

Upvotes: 10

Views: 2407

Answers (2)

Stephen Swensen
Stephen Swensen

Reputation: 22307

IMHO, you could save yourself some pain by using Unquote on top of NUnit. Then your assertion would be as simple as

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

NUnit's large suite of assertion overloads with sometimes unexpected runtime behavior are designed to compensate for C#'s relative lack of expressiveness compared to F#.

On-the-other-hand, because F# is already equipped with features such as structural comparison for elegantly expressing assertions, Unquote is designed to exploit its native features with just three simple assertion operators: test, raises, and raisesWith.

Upvotes: 4

Gene Belitski
Gene Belitski

Reputation: 10350

All you need to do in order for your original snippet to work is fixing f to have signature conformant to TestDelegate, which is unit -> unit. Just discard return value of File.GetAttributes:

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

F# compiler did not barf at your original code because just another fitting NUnit overload Assert.That(actual: obj, expression: Constraints.IResolveConstraint) exists.

As Assert.That has very broad usage I'd stick for testing expected exceptions to more specific assert form, for example:

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

where F# compiler would be able statically spot the wrong signature of your function.

Upvotes: 11

Related Questions