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?
Assert. Throws returns the exception that's thrown which lets you assert on the exception. var ex = Assert.
Catch is similar to Assert. Throws but will pass for an exception that is derived from the one specified. So use Assert. Catch if an exception that derives from the specified exception is valid (meaning that it too would be caught in an equivalent catch block).
Throws( Is. InstanceOf<ApplicationException>(), code ); // Allow both ApplicationException and any derived type Assert. Catch<ApplicationException>( code ); // Allow any kind of exception Assert. Catch( code );
Asserts that a condition is true. If the condition is false the method throws an AssertionException. That(Boolean, String) Asserts that a condition is true. If the condition is false the method throws an AssertionException.
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.
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With