My latest small project is to write a bunch of conversion functions in F#. Basically I want to convert things like, weights from kg to stones. Metres to feet and so on.
I wrote a couple of tests to ensure that the conversions were as expected, but ofcourse with such a set of functions and the wide range of values one might pass to them, my tests simply confirmed a couple of values were correct. It’d be nice to feel like I could say this function is valid for all values within the range x to y.
This is where FsCheck can help.
To use FsCheck just use nuget and search for FsCheck, I also use XUnit so chose FsCheck with XUnit support.
With FsCheck we create properties which define a valid state for a test, so for example if I have a function toMillimetres and another toKilometres, then we can expect that for every value entered into toMillimetres the function toKilometres will return the original value.
This can therefore be described as a property.
At this point we also need to beware that if the calculations involve floating point numbers we may not get back exactly the value we entered, so we’d use something like FsUnit to add a “tolerance” to the formula, i.e. it’s within 0.1 of the expected answer.
Let’s look at some code
[<Property>]
let ``From kilometre to millimetre and back`` () =
let property value =
let millimetres = km.toMillimetres (km.create value)
let kilometres = mm.toKilometres millimetres
kilometres
|> should (equalWithin 0.1) value
Check.QuickThrowOnFailure (testRange property)
So I’ve created an FsCheck property test named From kilometre to millimetre and back the idea being that (as previously stated) we can put a value into the toMillimetres function then put the result into the toKilometres function and expect (within a tolerance of 0.1) the value to be the same as the resultant kilometres.
The function Check.QuickThrowOnFailure takes our property and in this case, because I wanted to limit the range of the testing, the property goes through the function testRange (which is nothing very exciting) but I’ve listed it below for completeness
let testRange f value =
let inRange v = (v > -1E+10) && (v < 1E+10)
inRange value ==> lazy (f value)
The above code simple takes a function f (the property function from the test) and is supplied a value via FsCheck. We then simply ensure the value is within the range -1E+10 to 1E+10 by calling the inRange inner function.
Now when we run this property via FsCheck it will pass multiple values into the property function within the range we’ve defined and runs the unit test.
Note: In the property code I’m using FsCheck with XUnit and FsUnit with Xunit.