Suppose I am given a generator based on System.Random and I want to turn it into an FsCheck generator:
let myGen = MyGen(System.Random())
let fsGen = gen { return myGen.Generate() }
There are a couple of issues with this easy solution: the first is that the concept of size is ignored; I think it is not a big issue though, many generators ignore the size. The other issue impacts reproducibility because FsCheck generators are pure functions under the hood, the randomness is provided only by the sampling mechanism in the test runner. (this is clearly explained in this answer).
Now, a solution may be:
let fsGen =
gen {
let! seed = Gen.choose(0, System.Int32.MaxValue)
let myGen = MyGen(System.Random(seed))
return myGen.Generate() }
but there is a performance penalty because I have to create a new instance of MyGen each time (with a potentially high initialization cost)
Any better way?
Could the following work? Even though MyGen is random in nature, you can make it deterministic by fixing the seed:
let deterministicGen = MyGen(Random(42))
Due to the nature of Random, this isn't guaranteed to have a sufficiently random-like distribution, but if it does for your purposes, you can create a deterministic sequence of values generated by MyGen:
let deterministicValues = List.init 100 (fun _ -> deterministicGen.Generate())
This is only 100 values, but depending on your needs, you can create a bigger sample set of 1000, or perhaps even 10000 values.
Just like deterministicGen, deterministicValues is fixed: it's a list of values generated by MyGen.
You can easily ask FsCheck to randomly pick values from this list:
let fsGen = Gen.elements deterministicValues
Here, fsGen is an Gen<'a>, where 'a is whatever MyGen.Generate() returns.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With