Summary
Are there any events that can be run before every property case so that I can run setup and teardown for each run of a property?
Full version
I want to be able to test paired behaviors like "I can always fetch written records" or "output of readAllLines equals input to writeAllLines" with properties. I also want the property to not care how the operation sets are implemented (i.e. if any resources need to be cleaned up).
Each run of the property should
- be independent from other runs
- maintain state between calls of the operations within this single run
- not know about how how the operations maintain state
- not be a resource leak
I'm using FsCheck and Expecto. Examples will be in Expecto, but the problem isn't specific to the framework.
It is super easy to write this sort of setup and teardown with example-based tests. They take a predictable argument set, so I can run them in a wrapper that adds before and after events.
let testWithEnv setup cleanup name test =
let testWrap () =
let (api, env) = setup ()
test api
cleanup env
testCase name testWrap
The same can't be done with property tests. They have an unknown number of arguments that will largely be filled with random data.
I can apply the set of paired behaviors pretty easily, but any created resources like streams are left undisposed.
let testPropertyWithEnv setup cleanup name test =
let testWrap () =
let (api, env) = setup () // this is actually run once, but immutable so the individual runs don't leak state
test api // have to return this to pass along unapplied parameters
testProperty name testWrap
I've looked into
Runner eventsLooking at how to run FsCheck tests the closest hooks appear to be
OnStartFixture
which is only run once per test classOnArguments
is run after every pass and would potentially work to run cleanup
There is also the experimental Model-based testing features that could work. However, it seems really heavy considering I'm only concerned with the external consistency of operations. I do not want access to the backing state.
Giving up and inliningI can always write
testProperty "name" (fun arg1 arg2 ->
let (api,env) = setup ()
//test code here
cleanup env
)
but I'd like to avoid the boilerplate in every property and the exposure of the backing state.
IDisposablesDisposable objects also don't address the lack of setup hook.
More hands-on run loopI looked into ways of running the property tests in my wrapper, but the smallest runner Check.one
is for a single property, with no hooks between runs of the property.
Making the wrapper lazy also doesn't work testProperty name lazy(testWithSetup)