Thanks to Marcus Cuda, coordinator of the dnAnalytics project, I can end my little series (part 1 – part 2) with a nice result: the dnAnalytics team have decided to incorporate FsCheck in their testing. Cool! So next time, I’ll probably choose another project to “experiment” with (all in the interest of science), but this post I had lined up already.
Last time I described one kind of property based on invariants that hold over two or more related functions or methods. The example I gave was mathematically oriented (i.e. directly originating from the problem domain). Today I’ll show a property that should be familiar to many of you, and is applicable for quite a few of your types.
It happens that the Complex type in dnAnalytics can be constructed by parsing it from a string such as “1 + 3.0i”. On the other hand, calling ToString() on a Complex object produces a similar string. So, we specify that the ToString of a Complex value can be parsed, and produces the original value again:
let prop_ParseToString (c:Complex) = let actual = Complex.Parse(c.ToString(CultureInfo.InvariantCulture), CultureInfo.InvariantCulture) if Complex.IsInfinity(c) then Complex.IsInfinity(actual) elif Complex.IsNaN(c) then Complex.IsNaN(actual) else equalsUpTo 12 c actual
The only thing to watch out for is again the special values infinity and NaN. Also, since the ToString of a complex does not return a representation of Complex value up to its complete precision, we’re just checking equality up to a certain number of significant digits by using the equalsTo function.
In dnAnalytics version 0.3, running this property produces:
Parse ToString-Falsifiable, after 4 tests (0 shrinks): 0 + 1,79769313486232E+308i with exception: System.FormatException: One of the identified items was in a bad format
This bug was due to the fact that the string is split on the basis of the ‘+’ character – which is also used when a floating point number is printed in scientific notation – clearly visible in the counter example given by FsCheck. This is a confirmed bug that is solved by now. Apparently the same property also detected a bug when parsing NaN and Infinity values.
This again shows that writing properties, or specifications, for your code in FsCheck is easy, short, and most importantly, finds bugs!
The kind of property that is described here you can write for any pair of functions or methods that convert a representation A to a representation B and back (in this case, a Complex value to a string and back). It can be generalized as follows:
let toAndFro x transformTo transformFrom equals = x |> transformTo |> transformFrom |> equals x
Applied to the example:
let prop_ParseToString (c:Complex) = toAndFro c (fun c -> c.ToString(CultureInfo.InvariantCulture)) (fun s -> Complex.Parse(s, CultureInfo.InvariantCulture)) (fun c actual -> if Complex.IsInfinity(c) then Complex.IsInfinity(actual) elif Complex.IsNaN(c) then Complex.IsNaN(actual) else equalsUpTo 12 c actual)
(I was at TechDays 2009 in Antwerp this week, and watched a presentation about Pex by Peli de Halleux. He showed a similar pattern when using Pex to write parameterized unit tests. In fact, a lot of the patterns and scenarios he described sounded very recognizable to my ears. If you like FsCheck, definitely check out Pex as well.)
One project converted, a gazillion to go. If you maintain an open source project and are unsure or even better, skeptical! how FsCheck can be used for testing in your project, contact me and you’ll probably get me crazy enough to write some tests for you. I choose a scientific domain because F# is targeted towards that, but the applicability goes far beyond that, as I hope to have showed in this post. On the other hand, I am also curious for what kind of projects or domains you are already using FsCheck. Did you encounter testing patterns like “toAndFro” that you’d like to share? Got any wishes? Don’t hesitate to let me know. Or even better, spread the word and blog about FsCheck yourself.
|Share this post :|