I am attempting to initialize my model entities using FsCheck. The models live in C# and are normally initialized via Entity Framework via their private setters. For example (contrived):
public class Model
{
public string One { get; private set; }
public int Two { get; private set; }
}
I would like to create an FsCheck generator that automatically uses the registered generator for each property to produce the model. Something like this:
let modelGenerator =
gen {
let incident = new Model()
typeof<Model>.GetProperties()
|> Array.filter (fun p -> p.CanWrite)
|> Array.iter (fun p ->
let! newVal = Arb.generateType p.PropertyType // I wish I could do this
p.SetValue(incident, newVal))
return incident
}
There are two things wrong with this:
let!
can't be used outside of the gen
computation expression.Arb.generateType
doesn't exist, and I can't find a way to do its equivalentIs it possible to create a generator that will automatically set the private fields on my model?
With the power of reflection, everything is possible (or throws at runtime).
module Arb =
open System.Reflection
// this is just a helper type to do reflection on.
type internal GenerateInvoker =
static member Invoke<'typ> () =
Arb.generate<'typ>
|> Gen.map box
// Invokes a generic method using a runtime type as a generic argument.
let generateType (typ: Type) =
typeof<GenerateInvoker>
.GetMethod("Invoke", BindingFlags.Static ||| BindingFlags.NonPublic)
.MakeGenericMethod([|typ|])
.Invoke(null, [||]) :?> Gen<obj>
let modelGenerator =
gen {
let incident = new Model()
let props =
typeof<Model>.GetProperties()
|> Array.filter (fun p -> p.CanWrite)
// gen builder implements For, so you can do something like this.
for prop in props do
let! newVal = Arb.generateType prop.PropertyType
prop.SetValue(incident, newVal)
return incident
}
Gen.sample 1 3 modelGenerator