Property Based Testing without a Property Based Testing framework by Mark Seemann
Sometimes, you don't need a Property-Based Testing framework to do Property-Based Testing.
In my previous post, I showed you how to configure FsCheck so that it creates char values exclusively from the list of the upper-case letters A-Z. This is because the only valid input for the Diamond kata is the set of these letters.
By default, FsCheck generates 100 random values for each property, and runs each property with those 100 values. My kata code has 9 properties, so that means 900 function calls (taking just over 1 second on my Lenovo X1 Carbon).
However, why would we want to select 100 random values from a set of 26 valid values? Why not simply invoke each property (which is a function) with those 26 values?
That's not so hard to do, but if there's a way to do it with FsCheck, I haven't figured it out yet. It's fairly easy to do with xUnit.net, though.
What you'll need to do is to change the Letters type to an instance class implementing seq<obj[]> (IEnumerable<object[]> for the single C# reader still reading):
type Letters () = let letters = seq {'A' .. 'Z'} |> Seq.cast<obj> |> Seq.map (fun x -> [|x|]) interface seq<obj[]> with member this.GetEnumerator () = letters.GetEnumerator() member this.GetEnumerator () = letters.GetEnumerator() :> Collections.IEnumerator
This is simply a class that enumerates the char values 'A' to 'Z' in ascending order.
You can now use xUnit.net's Theory and ClassData attributes to make each Property execute exactly 26 times - one for each letter:
[<Theory; ClassData(typeof<Letters>)>] let ``Diamond is as wide as it's high`` (letter : char) = let actual = Diamond.make letter let rows = split actual let expected = rows.Length test <@ rows |> Array.forall (fun x -> x.Length = expected) @>
Instead of 900 tests executing in just over 1 second, I now have 234 tests executing in just under 1 second. A marvellous speed improvement, and, in general, a triumph for mankind.
The point is that if the set of valid input values (the domain) is small enough, you may consider simply using all of them, in which case you don't need a Property-Based Testing framework. However, I still think this is probably a rare occurrence, so I'll most likely reach for FsCheck again next time I need to write some tests.