Phil Trelford's Array
POKE 36879, 255

F# as a Unit Testing Language

November 21, 2012 14:24 by phil

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


Tags:
Categories: F# | C# | .Net
Actions: E-mail | Permalink | Comments (6) | Comment RSSRSS comment feed

Comments

November 21. 2012 16:39

Mathias Brandewinder

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 :) Does it support setting up to throw exceptions?

Mathias Brandewinder

November 21. 2012 21:24

Carsten

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 :D

Carsten

November 21. 2012 23:31

Phil

@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

November 21. 2012 23:33

Phil

@Carsten,

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

Phil

January 27. 2013 00:08

trackback

Testing and mocking your C# code with F#

Testing and mocking your C# code with F#

Clear Lines Blog

February 13. 2013 01:38

trackback

Mocking with Foq

Mocking with Foq

Phil Trelford's Array

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading