Recently I discovered the xUnit.NET framework for unit testing, and it is a very nice minimalist framework for TDD. I especially like writing tests for F# code using it. It allows me to write very short test suites against any CLR library, in most .NET languages. For example:
#light
open Xunit
open NT.Model
let sut = new Person("Jim")
[<Fact>]
let jim_is_a_geek() = Assert.True(sut.IsAGeek)
The Person class could be written in C# or VB and used here. Also note that the ultimate class name of this test is inferred by the filename. I could add explicit module and namespace information if necessary.
In something like MbUnit / NUnit / Gallio I have to use the following OO style syntax...
#light
namespace NT.Model.Specs
module PersonSpecs
open MbUnit
open NT.Model
[<TestFixture>]
type jims_behaviours() =
let sut = new Person("Jim")
[<Test>]
member j.is_a_geek() = Assert.True(sut.IsAGeek)
Note that the namespace and module must be declared in order for the gallio runner to discover the tests. I think this might be a bug...
However, regardless of the framework you use, F# also lends itself well to setting up for customized approaches to testing. I feel this has some potential for BDD testing in F#. Take the following example:
#light
open Xunit
let MustEqual expected actual = Assert.Equal(expected, actual)
[<Fact>]let a_fluent_assertion() = 0 |> MustEqual 0
Taking this one step further we can take advantage of operator overloading to provide extremely terse tests. Note that I don't actually advocate doing this, only pointing out that its possible:
#light
open Xunit
let (&=) expected actual = Assert.Equal(expected, actual)
let (&!=) notExpected actual = Assert.NotEqual(notExpected, actual)
[<Fact>]
let an_obscure_assertion() = 0 &!= 1
Based on these samples, I think it is clear that without much effort, a fluent specification interface could be arrived at, perhaps even a domain specific one at that.
F# record types are readily testable also:
#light
open Xunit
type Person =
{ name : string;
age : int;
}
let Jim = { name="Jim"; age=28 }
let Fred = { name="Fred"; age=52 }
let IsNot expected actual = Assert.NotEqual(expected, actual)
[<Fact>]
let jim_is_not_fred() = Jim |> IsNot Fred
Sometimes, the assertion framework isn't needed, but having a test runner is still obviously handy. For the next example I setup a discriminated union and then use pattern matching to determine the outcome of the test simply using the built in failwith keyword:
#light
open Xunit
type Animal =
| Dog
| Cat
type Person =
{ name : string;
age : int;
pet : Animal; }
let Jim = { name="Jim"; age=28; pet=Cat; }
[<Fact>]
let Jim_cannot_own_cats() =
match Jim.pet with
| Cat -> failwith("Jim owns a cat!")
| _ -> ()
A snippet from the xUnit console runner output for this failing test:
Tests failed:
1) Jims_specifications.jim_cannot_own_cats : Microsoft.FSharp.Core.FailureException : Jim owns a cat!
On that...hopefully soon tools like Gallio will provide us with some F# support in Visual Studio for the Resharper test runner or even MSTest. At current support for xUnit testing with Gallio in F# projects appears to be a non starter. Alternatively, latest SharpDevelop betas have inbuilt F# and NUnit support which I find to be quite good.
In the meantime we can go all 'old school' on VS 2008 to provide some ease of use.
In the following screen shot I've added the xUnit.NET console runner to external tools by selecting from the menu, Tools, External Tools...
Next I like to map this to a keyboard chord, say Ctrl+R,Ctrl+X:
So that now after building, I can run my entire test suite using my keyboard shortcut to yield a testing experience that is 'good enough' for TDD/BDD development in F#, well, that's my opinion of course :)
