3

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?

Community
  • 1
  • 1
Giacomo Citi
  • 198
  • 1
  • 8
  • I suppose redesigning `MyGet` to be deterministic is out of the question? – Mark Seemann Sep 30 '15 at 11:52
  • As long as `MyGen` takes the `Random` instance from the outside we can turn it into a deterministic function by passing `Random(42)` like you suggested, or by passing an instance built from the FsCheck _random context_ like in my example – Giacomo Citi Sep 30 '15 at 12:52

1 Answers1

4

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.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • This is a clever idea! Not the definitive solution because in some cases you may not want to limit the choice to a fixed set; but in many cases it may be a very good compromise. Thanks. – Giacomo Citi Sep 30 '15 at 12:43