Reputation: 36738
While searching for an answer, I've found several questions that don't quite match my situation — so I'll ask a new one.
I am writing some FsCheck tests for a data structure, where I want to check about twenty different properties on each copy of the data structure I construct. What I've done so far is to write a predicate for each property, then I've made a list of the predicates and I'll call them each in turn with List.forall
, like so:
module PropertyChecks =
let ``Tail length plus tree length should equal vector length`` vec =
treeLength vec.root + Array.length vec.tail = vec.len
let ``The tail may only be empty iff the vector is empty`` vec =
(vec.len = 0) = (Array.length vec.tail = 0)
let ``The tail's length may not exceed tailMax`` vec =
Array.length vec.tail < tailMax
let ``If vec.len <= tailMax, all items are in tail and root is empty`` vec =
(vec.len > tailMax) || (Array.length vec.root = 0)
// And so on for about 15 more predicates
let allProperties =
[
``Tail length plus tree length should equal vector length``
``The tail may only be empty iff the vector is empty``
``The tail's length may not exceed tailMax``
``If vec.len <= tailMax, all items are in tail and root is empty``
]
let checkProperties vec =
allProperties |> List.forall (fun pred -> pred vec)
// Rest of my code omitted since it's not relevant to the question
The problem I'm facing is that I expect that when one property fails because I didn't construct the data structure properly, two or three other properties will fail at the same time. I'd like to get a list of all failing properties, which means that in checkProperties
I'd like to extract the name of the failing predicate. I've seen multiple answers along the lines of "You can't get the MethodInfo
from an arbitrary F# function that you received as an argument, because you never know whether you got the function itself, or a lambda, or an anonymous function". But here, I not only know that I have real functions, I know what their names are. I could easily copy and paste their names into strings, and make allProperties
a list of (string,function) tuples. But I'm already feeling not quite happy with having copied-and-pasted once (to put the predicates into that list), and I'd rather not do it twice.
What's a better solution that making a list of (function name, function) tuples? Could I move allProperties
and checkProperties
out of the PropertyChecks
module so that it contains nothing but predicates, and then use reflection to walk that module?
I'm very new to the entire .Net reflection system, so I might well be missing something obvious. Please feel free to point out the obvious if I'm missing it; I won't feel insulted.
Upvotes: 3
Views: 99
Reputation: 243096
The best option for running FsCheck tests is to use FsCheck together with some unit test runner. The test runner takes care of finding all the properties, running them and printing nice error logs when something goes wrong.
The two most common test runners in .NET that both work with FsCheck are xUnit and NUnit and the FsCheck documentation describes how to use them:
In both cases, you mark the properties with the [<Property>]
attribute and the test runner will use this to find them and run them using FsCheck for you. So you'll need something like:
[<Property>]
let ``Tail length plus tree length should equal vector length`` vec =
treeLength vec.root + Array.length vec.tail = vec.len
[<Property>]
let ``The tail may only be empty iff the vector is empty`` vec =
(vec.len = 0) = (Array.length vec.tail = 0)
[<Property>]
let ``The tail's length may not exceed tailMax`` vec =
Array.length vec.tail < tailMax
[<Property>]
let ``If vec.len <= tailMax, all items are in tail and root is empty`` vec =
(vec.len > tailMax) || (Array.length vec.root = 0)
Upvotes: 5
Reputation: 25526
Here is a hacky bash script that might be easier than reflection:
#!/bin/bash
echo "let pairs = [|";
cat t.fs | grep let | grep -o "\`\`[^\`]*\`\`" | tr -d \` | awk '{printf " (\"%s\",``%s``);\n", $0,$0}';
echo "|]"
which gives:
let pairs = [|
("Tail length plus tree length should equal vector length",``Tail length plus tree length should equal vector length``);
("The tail may only be empty iff the vector is empty",``The tail may only be empty iff the vector is empty``);
("The tail's length may not exceed tailMax",``The tail's length may not exceed tailMax``);
("If vec.len <= tailMax, all items are in tail and root is empty",``If vec.len <= tailMax, all items are in tail and root is empty``);
|]
Upvotes: 2