There are a few different libraries that provide test assertions for F#. I went through a couple today and tried a trivial example in each.
I’m using xUnit for these examples, but all of this should apply to NUnit (and other test runners) too. I’ve put all the code in a Sample.fs gist if you want to see it all in one place.
xUnit assertions
I’ve written about getting started with NUnit in F# before. We can also use xUnit and its built in assertions.
I needed to specify the int list
type explicitly to get F# to resolve the correct overload.
Here’s an example of an assertion failure (when I change the expected value to [2;3;5]
):
Position: First difference is at position 2
Expected: FSharpList<Int32> { 2, 3, 5 }
Actual: FSharpList<Int32> { 2, 3, 4 }
FsUnit
FsUnit provides helpers for NUnit, xUnit, MbUnit, and MSTest assertions to make them play nicely with F# syntax and type inference. I installed the FsUnit.Xunit
package.
Sample failure:
Position: First difference is at position 0
Expected: Equals [2; 3; 5]
Actual: was [2; 3; 4]
(I’m not sure why first difference is at position 0 here?)
Unquote
Unquote lets us use quoted expressions for assertions.
If the test fails, Unquote shows each step in reducing the expression so you can see where they start to differ:
List.map Sample.incr [1; 2; 3] = [2; 3; 5]
[2; 3; 4] = [2; 3; 5]
false
This case only shows 3 steps, but more complex expressions will show more.
FsCheck
FsCheck is influenced by Haskell’s QuickCheck and Scala’s scalacheck. Rather than asserting a specific input and output, we define a property that should hold for all values of a type (optionally requiring they meet certain criteria, such as being a positive integer).
The FsCheck.Xunit package has specific support for xUnit through a PropertyAttribute
that let us run properties directly as an xUnit test (otherwise a little more boilerplate is required, see “Using FsCheck with other testing frameworks” in the Quick Start guide).
If we modify the property to ensure it fails (for example, (List.map f << List.map g) xs = List.map (f << g) (List.filter even xs)
), we get this output:
FsCheck.Xunit.PropertyFailedException
Falsifiable, after 3 tests (2 shrinks) (StdGen (267259328,295888818)):
[1]
This shows that given an input of [1]
the property does not hold.
Fuchu
Fuchu is more focussed on test organisation than assertions, and can be used with any of the assertion-providing libraries above. If you’d like to try something different to the usual (N|x|Mb)Unit approaches for defining test cases then give it a look.