I’ve found writing unit tests in F# very productive, with it’s terse syntax and powerful type inference. I write F# tests against C# and F# implementations.
Writing tests in F# can be a good introduction to the language and this is something we do on our team. To get up to speed quickly you’ll typically want a familiar unit testing framework and mocking library syntax.
F# code works with all the popular open source .Net unit testing frameworks:
…it also works with Microsoft’s Visual Studio Unit Test Framework.
F# unit tests can be run inside Visual Studio using TestDriven.Net, ReSharper and NCrunch.
Backticks
F# lets you write function names with white space inside double backtick marks (``):
let [<Test>] ``1 + 1 should equal 2`` () =
Assert.AreEqual(1 + 1, 2)
This feature alone can make your unit tests easier to read and write.
Void
In C# the return value of a method can be implicitly ignored. In F# return values must be explicitly handled. Simple test functions are typically expected to return nothing, so in F# you need to be careful that you don’t inadvertently return a value, for example NUnit’s Assert.Throws returns the specified exception which you can pipe to ignore. Or you can extend the Assert class with a Raise method that ignores the return value.
module Assert =
/// Verifies that a delegate raises the specific exception when called.
let Raises<'TExn when 'TExn :> exn> (f:unit->unit) =
Assert.Throws<'TExn>(TestDelegate(f)) |> ignore
F# Object Expressions
F# Object Expressions let you implement an interface without the need to create a new class. This can be useful for simple mocking scenarios:
let comparer = { new IComparer<_> with member x.Compare(l,r) = l - r }
This works well for small interfaces but does not scale well to larger or changing interfaces as all members must be implemented:
let list =
{ new IList<_> with
member __.Count = 1
member __.Insert(_,_) = raise <| NotImplementedException()
member __.IndexOf(_) = raise<| NotImplementedExcpetion()
// ...
}
.Net Mocking libraries
F# works well with many of the major .Net mocking libraries like Rhino.Mocks and NSubstitute. Built-in support for LINQ expression trees used in libraries like Moq was introduced recently with F# 3.0 in Visual Studio 2012. For F# 2.0 in Visual Studio 2010 the F# PowerPack is required which can be a bit fiddly.
Foq
Foq is an F# mocking library similar to Moq that parses F# Code Quotations, making it compatible with F# 2.0/3.0 and .Net 2.0/3.5/4.0/4.5.
Moq C# Method:
var mock = new Mock<IFoo>();
mock.Setup(foo => foo.DoSomething("ping")).Returns(true);
var instance = mock.Object;
Foq F# Method:
let foo = Mock<IFoo>()
.Setup(fun foo -> <@ foo.DoSomething("ping") @>).Returns(true)
.Create()
Moq C# Matching Arguments:
mock.Setup(foo => foo.DoSomething(It.IsAny<string>())).Returns(true);
Foq F# Matching Arguments:
mock.Setup(fun foo -> <@ foo.DoSomething(any()) @>).Returns(true)
Moq C# Property:
mock.Setup(foo => foo.Name ).Returns("bar");
Foq F# Property:
mock.Setup(fun foo -> <@ foo.Name @>).Returns("bar")
Summary
Unit testing, including mocking, is well supported in F#. If you are a .Net developer you should find the experience familiar and perhaps a good opportunity to pick up a new language.
Further reading