Testing 如何为System.Type创建任意类型?
我正在尝试使用FsCheck初始化模型实体。这些模型生活在C#中,通常通过其私有setter通过实体框架进行初始化。例如(人为的): 我想创建一个FsCheck生成器,它自动为每个属性使用注册的生成器来生成模型。大概是这样的:Testing 如何为System.Type创建任意类型?,testing,f#,fscheck,Testing,F#,Fscheck,我正在尝试使用FsCheck初始化模型实体。这些模型生活在C#中,通常通过其私有setter通过实体框架进行初始化。例如(人为的): 我想创建一个FsCheck生成器,它自动为每个属性使用注册的生成器来生成模型。大概是这样的: let modelGenerator = gen { let incident = new Model() typeof<Model>.GetProperties() |> Array.f
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
}
let modelGenerator=
根{
让事件=新模型()
typeof.GetProperties()
|>Array.filter(fun p->p.CanWrite)
|>Array.iter(乐趣p->
let!newVal=Arb.generateType p.PropertyType//我希望我能这样做
p、 设置值(事件,新值))
返回事件
}
这有两个问题:
let代码>不能在gen
计算表达式之外使用
Arb.generateType
不存在,我也找不到一种方法来实现它的等效功能有没有可能创建一个生成器来自动设置我的模型上的私有字段?有了反射功能,一切都是可能的(或者在运行时抛出)
模块Arb=
开放系统。反射
//这只是一个要进行反射的助手类型。
类型内部GenerateInvoker=
静态成员调用
|>通用地图盒
//使用运行时类型作为泛型参数调用泛型方法。
let generateType(类型:Type)=
类型
.GetMethod(“调用”,BindingFlags.Static | | | BindingFlags.NonPublic)
.MakeGenericMethod([|类型]
.Invoke(null,[| |]):?>Gen
让modelGenerator=
根{
让事件=新模型()
让道具=
typeof.GetProperties()
|>Array.filter(fun p->p.CanWrite)
//gen builder实现了,所以您可以这样做。
对于道具中的道具呢
let!newVal=Arb.generateType prop.PropertyType
属性设置值(事件,新值)
返回事件
}
Gen.sample 1 3模型生成器
在我向GetMethod调用添加绑定标志后效果非常好:typeof.GetMethod(“Invoke”,BindingFlags.Static | | | BindingFlags.NonPublic)
@Daws:对,我可能在测试后对答案做了太多的修改。
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
}
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