F# and xUnit Class Fixtures
Startup and teardown test modules
Why am I doing this?
I use a combination of F#, xUnit, and FsUnit to do my unit testing on .NET. I want to run a database setup script at the start of some test modules. In this case, according to the xUnit documentation, I need to use IClassFixture (or ICollectionFixture). I recently wanted to figure out how to make this happen in my unit tests. This is an example of how to do it with F#.
xUnit IClassFixture
Using F# with xUnit Interfaces
For writing tests I don't usually use the xUnit interfaces so my test modules are just a bunch of functions rather than test classes. Kinda like this:
namespace Demo.Tests
open Xunit
open FsUnit.Xunit
open System
module TempTests =
[<Fact>]
let test () =
"STUFF" |> should equal "OTHER STUFF"
But in order to take advantage of the setup and teardown features with IClassFixture I will have to write my test modules like classes. Following the guide from xUnit we first need a fixture class. So in F# that will be a fixture type declared as a class like this:
type DemoFixture() =
//DO THE TEST SETUP HERE
//(THIS IS THE CONSTRUCTOR)
do
sprintf "You can do your test setup in this do block." |> ignore
interface IDisposable with
member this.Dispose () =
//CLEAN UP TEST DATA OR WHATEVER YOU NEED TO CLEANUP YOUR TESTS
()
Here, in the constructor I can do any test setup I might need to do, like database queries. In my case I like to run a stored procedure that clears the database and creates default data to work with in my tests.
Next we need a class for the tests. This class will implement IClassFixture with the type of fixture you want to use in this class. Using the above fixture, we can write something like this.
type DemoTests() =
[<Fact>] //THIS IS A TEST
member __.``Can create a start node`` () =
//DO THE TEST STUFF
"something" |> should equal "something"
interface IClassFixture<DemoFixture>
Here I declared a new type, DemoTests, as a class and implemented the empty IClassFixture interface as type DemoFixture. And I can add member functions, which are my test functions, the Facts.
When I put this all together it looks like this:
namespace Demo.Tests
open Xunit
open System
open FsUnit.Xunit
module Fixtures =
type DemoFixture() =
//DO THE TEST SETUP HERE
//(THIS IS THE CONSTRUCTOR)
do
sprintf "You can do your database stuff here or whatever you like" |> ignore
interface IDisposable with
member __.Dispose () =
//CLEAN UP TEST DATA OR WHATEVER YOU NEED TO CLEANUP YOUR TESTS
()
module DemoTests =
type DemoTests() =
[<Fact>]
member __.``Can create a start node`` () =
//DO THE TEST STUFF
"INPUT" |> should equal "RESULT"
interface IClassFixture<Fixtures.DemoFixture>
Reusablility and CollectionFixture
To make the setup class reusable I can generalize it and put it in a separate module. And then for my test modules I can follow the above pattern.
Alternatively we can use the ICollectionFixture xUnit class. This pattern looks basically like how we use IClassFixture with an extra collection definition class.
My collection class looks like this:
[<CollectionDefinition("Build graphs collection")>]
type DemoCollection() =
interface ICollectionFixture<DemoFixture>
Then the tests class would change to implement IClassFixture like this.
[<Collection("Build graphs collection")>]
type DemoTests() =
[<Fact>]
member __.``Can create a start node`` () =
//DO THE TEST STUFF
"INPUT" |> should equal "RESULT"
interface ICollectionFixture<DemoFixture>
F# and FsUnit
F# and FsUnit give us some nice readability features.
Double back tick
Yep, I can define a function, in double back ticks, as a readable sentence. In this case I have, Can create a start node. Awesome, right? This is a feature of F# so I an declare functions like this in my regular code, too.
should equal
This is a feature of FsUnit. FsUnit gives us a more functional way of defining our test assertions, which also reads nicer and more natural. So instead of
Assert.Equal("something", "something")
I can write:
"something" |> should equal "something"
Thoughts
Using xUnit and F# together I get all the great functional features of F# without losing any of the features of xUnit. This makes writing unit tests more fun and easier.
There are other advantages of using F# for testing. I haven't used it yet but we can do property testing with FsCheck, and in F# we can mock objects and types in nice ways. And we have canopy, a web testing framework. I hope to experiment more with all of these in the near future.
Links
xUnit
FsUnit
Stack Overflow discussions
Building stuff that works with F#- Tomas Petricek
Full Stack .NET Programmer and Ham
Cover image credit: https://sciencevsmagic.net/fractal/#0060,0090,1,1,0,0,1