I use FsCheck for property-based testing, so I defined a set a generators for custom types. Some of types are composed of others, and there are generators for all of them. Having defined a generator for Alphanumeric type, I want to define a generator for RelativeUrl type, and RelativeUrl is list of 1-9 Alphanumeric values separated by slash symbol. Here's the definition that works (Alpanumeric has "Value" property that converts it to String):
static member RelativeUrl() =
Gen.listOfLength (System.Random().Next(1, 10)) <| Generators.Alphanumeric()
|> Gen.map (fun list -> String.Join("/", list |> List.map (fun x -> x.Value)) |> RelativeUrl)
Even though it's quite simple I don't like that I use Random.Next method instead of using FsCheck random generators. So I tried to redefine it like this:
static member RelativeUrl_1() =
Arb.generate<byte>
|> Gen.map int
|> Gen.suchThat (fun x -> x > 0 && x <= 10)
|> Gen.map (fun length -> Gen.listOfLength length <| Generators.Alphanumeric())
|> Gen.map (fun list -> String.Join("/", list))
Compiler accepts it but in fact it's wrong: a "list" in the last statement is not a list of Alphanumeric values but a Gen. Next attempt:
static member RelativeUrl() =
Arb.generate<byte>
|> Gen.map int
|> Gen.suchThat (fun x -> x > 0 && x <= 10)
|> Gen.map (fun length -> Gen.listOfLength length <| Generators.Alphanumeric())
|> Gen.map (fun list -> list |> Gen.map (fun elem -> String.Join("/", elem |> List.map (fun x -> x.Value)) |> RelativeUrl))
But this doesn't work either: I am getting back Gen of Gen of RelativeUrl, not Gen of RelativeUrl. So what would be a proper way of combining generators at different levels?
System.Random
, can't you useGen.choose
? – Mark Seemannchoose
instead ofRandom
by instantly sampling it, but that would defeat the purpose, because thischoose
won't be part of the resulting generator, but will work as a sort of utility function. No better thanRandom
, really. – Fyodor SoikinSystem.Random
in generators because it will destroy reproducibility (i.e. same seed returns different results each time) and make shrinking impossible (or at least non-deterministic). – Kurt SchelfthoutGen.choose
instead of Random. If you plug it in place of Random in the OP's first code block, it would be useless, would it not? – Fyodor SoikinGen.choose |> Gen.sample
it's no better than Random. But don't think that's what @MarkSeemann meant :) Confusion all round...but I think we are all saying the same thing. – Kurt Schelfthout