Phillip Trelford's Array

POKE 36879,255

F# as a Unit Testing Language

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

Comments (4) -

  • Mathias Brandewinder

    11/21/2012 4:39:36 PM |

    Funny - I was just thinking yesterday of writing a blog post on F# and unit testing. It's not an aspect of the language that gets much press, but after using FsUnit for a while on F# projects, I find it adds lots of expressiveness to the unit tests, by comparison to raw NUnit, and it might actually be a nice entry point to the language for beginners. I found most C# developers are uncomfortable with the |> operator, but in the context of FsUnit, it is IMO extremely natural, more so than a classic fluent interface.
    Nice job on Foq, haven't tried it yet but the code looks nice. And the name is pretty daring Smile Does it support setting up to throw exceptions?

  • Carsten

    11/21/2012 9:24:48 PM |

    As there are plugins/extensions for NUnit and XUnit you can use now the inbuild VS2012 test runner (of course also with F# libs) and it's quite nice.
    It comes with the ability to run the tests on build and it's clever enough to only rerun red tests on the next build.
    Overall I now prefer it to NCrunch (the compile on each save just slows VS down to much for me as I have ReSharper installed already) - the only downside is it's somewhat poor output but I'm sure there is improvement on the was Laughing

  • Phil

    11/21/2012 11:31:01 PM |

    @Mathias,

    Thanks! Yes, Foq supports exceptions, e.g. set up the Item property setter to always raise an exception:

    .Setup(fun x -> <@ x.Item(any()) <- any()  @>).Raises<ApplicationException>()

    There's also support for setting up events.

  • Phil

    11/21/2012 11:33:46 PM |

    @Carsten,

    I've been using and liking the VS2012 test runner too. Across the team all the mentioned test runners are in use Smile

Pingbacks and trackbacks (2)+

Comments are closed