FSharpSpec is a BDD framework that leverages the succinctness of F# to create specifications without the noise.
Another specification framework? Why do I care?
Well, the examples speak for themselves. (They can be found in their entirety here)
Assuming we have a system under test:
let sut = new StringCalculator()
Write a simple spec:
member x.``adding empty string`` = it "returns 0" (sut.Add "") should.equal 0
Produce specs however you like
Since FSharpSpec verifies a list of specifications that are returned by an F# property, we can produce this list however we like. This allows us, among other things, to achieve super compact code and maximum code reuse.
We can use this helper function:
let testAdding (nums:string) expected = let specName = (sprintf "adding '%s' returns %d" nums expected) it specName (sut.Add nums) should.equal expected
to write these specs:
member x.``handles \n separator`` = [ testAdding "1\n1" 2 testAdding "1\n2\n3\n4" 10 ]
Additionally this allows us to use F#s capabilities to do all kind of other neat things - the possibilities are endless (did I just say that?). For instance ...
By mapping values to specifications:
member x.``single numbers`` = ["0", 0; "1", 1; "5", 5; "10", 10; "99", 99; "100", 100; "999", 999 ] |> List.map (fun (num, r) -> it (sprintf "adding '%s' returns %d" num r) (sut.Add num) should.equal r)
Multiple assertions without the price
In case you were wondering if all specifications get evaluated, even if one of them fails ...
Each 'it' is wrapped and executed independently from all other 'it's, and therefore if one fails, all the others still are evaluated.
No catch? - well, non-spec related errors (like null references) can make it impossible to create the spec list in the first place, but even that case can be dealt with.
BDD oriented output
The FSharp spec runner takes context inheritance into account to produce a tree-like output that visualizes this information. This makes it much easier to see under which circumstances specifications are failing and provides better documentation of our code.
+ Given a TestType(0) | + named ContextName | | | - when I increment by 1 | - should have Value 1 | + named ContextName | | + that has been incremented | | | - when I increment by 1 | | - should have Value 2 | | + that has been incremented | | | + and was incremented again | | | | | | | - when I increment by 1 | | | - should have Value 3
Tons of more features
Furthermore FSharpSpec has support for dealing with exceptions, string and list queries and more, as explained in the more detailed documentation.
Read on ...
Getting Started with FSharpSpec
Downloading and Building FSharpSpec
Build from Source
The best way to obtain the source code for FSharpSpec is to clone its Git repository available via:
git clone git://github.com/thlorenz/FSharpSpec.git
Native FSharpSpec Runner
FSharpSpec comes with a built in runner that can be invoked from the command line or from within Visual Studio via an external command to run specification assemblies. Debugging specifications is not supported when they are run this way.
Of course there is nothing keeping you from directly using the FSharpSpec.RunnerUtils in order to create a short F# program that runs your tests and thus enables debugging. It's quite simple - really, as can be seen from the built in FSharpSpec Runner
At this point TD.Net can be used to run entire specification assemblies in normal and debug mode. Unfortunately TD.Net seems to have problems identifying F# namespaces and types, so those cannot be run individually.
In the works (whenever I get around to implement their huge interface).