FsCheck 惰性生成器
FsCheck lazy generators
我在测试中生成数据时遇到问题。
testProperty "calculate Operation against different operations should increase major" <| fun operationIdApi operationIdClient summaryApi summaryClient descriptionApi descriptionClient ->
( notAllEqual [
fun () -> assessEquality <| StringEquals(operationIdApi, operationIdClient)
fun () -> assessEquality <| StringEquals(summaryApi , summaryClient)
fun () -> assessEquality <| StringEquals(descriptionApi, descriptionClient)
]) ==> lazy (
let operationClient = createOpenApiOperation operationIdClient summaryClient descriptionClient
let operationAPI = createOpenApiOperation operationIdApi summaryApi descriptionApi
let actual = calculate operationAPI operationClient
Expect.equal actual (Fact.Semver.IncreaseMajor) "return IncreaseMajor"
)
实际测试的代码是:
semver {
if operationAPI.OperationId<> operationClient.OperationId then yield! IncreaseMajor
if operationAPI.Summary <> operationClient.Summary then yield! IncreaseMajor
}
当生成的数据具有相同的 OperationId、相同的摘要和不同的描述时,测试应该会失败。
但事实并非如此,这促使我创建了自己的生成器或至少尝试这样做:
我希望我的测试是这样写的:
testProperty "calculate Operation against different operations should increase major" <| fun (operationId:ElementSet<string>) (summary:ElementSet<string>) ->
因此我相应地创建了一个类型:
type ElementSet<'a> =
| Same of 'a
| Different
和这种类型的生成器:
let setGen<'a> =
Gen.oneof [
gen {
let! v = Arb.generate<'a>
return Same(v)
}
gen { return Different}
]
type ElementSetGenerator =
static member ElementSet() =
Arb.fromGen setGen<'a>
do Arb.register<ElementSetGenerator>() |> ignore
然后我试图提取数据来构建我的对象:
let createOpenApiOperation operationId summary=
let pi = OpenApiOperation(OperationId=operationId.Get, Summary=summary.Get)
pi
Get 方法尚不存在,所以我准备通过向我的 ElementSet<'a> 添加一个成员来实现它:
type ElementSet<'a> =
| Same of 'a
| Different
with member this.Get =
match this with
| Same s -> s
| Different -> Arb.generate<'a>// some random generation here
这就是我被困的地方。当我提取数据时,我很想在这里获得一些随机性。我想知道这样做是否正确,或者我是否应该早点回答这个问题?
感谢您的意见。
我想我找到了,答案是一开始就处理它:
let setGen<'a when 'a:equality> =
Gen.oneof [
gen {
let! v = Arb.generate<'a>
return Same(v)
}
gen {
let! x,y =
Arb.generate<'a>
|> Gen.two
|> Gen.filter (fun (a,b)-> a <> b)
return Different(x,y)
}
]
然后使用两个 getter 来访问值:
type ElementSet<'a> when 'a:equality=
| Same of 'a
| Different of 'a*'a
with member this.Fst = match this with | Same s -> s | Different (a, b)-> a
member this.Snd = match this with | Same s -> s | Different (a, b)-> b
这样我就可以在我的测试中访问值:
testProperty "calculate Operation against different operations should increase major" <| fun (operationId:ElementSet<NonWhiteSpaceString>) (summary:ElementSet<NonWhiteSpaceString>) (description:ElementSet<NonWhiteSpaceString>) ->
let operationClient = createOpenApiOperation operationId.Fst summary.Fst description.Fst
let operationAPI = createOpenApiOperation operationId.Snd summary.Snd description.Snd
let actual = calculate operationAPI operationClient
Expect.equal actual (Fact.Semver.IncreaseMajor) "return IncreaseMajor"
为了记录,我创建了我的存根,如下所示:
let createOpenApiOperation (operationId:NonWhiteSpaceString) (summary:NonWhiteSpaceString) (description:NonWhiteSpaceString)=
let pi = OpenApiOperation(OperationId=operationId.Get, Summary=summary.Get, Description=description.Get)
pi
我在测试中生成数据时遇到问题。
testProperty "calculate Operation against different operations should increase major" <| fun operationIdApi operationIdClient summaryApi summaryClient descriptionApi descriptionClient ->
( notAllEqual [
fun () -> assessEquality <| StringEquals(operationIdApi, operationIdClient)
fun () -> assessEquality <| StringEquals(summaryApi , summaryClient)
fun () -> assessEquality <| StringEquals(descriptionApi, descriptionClient)
]) ==> lazy (
let operationClient = createOpenApiOperation operationIdClient summaryClient descriptionClient
let operationAPI = createOpenApiOperation operationIdApi summaryApi descriptionApi
let actual = calculate operationAPI operationClient
Expect.equal actual (Fact.Semver.IncreaseMajor) "return IncreaseMajor"
)
实际测试的代码是:
semver {
if operationAPI.OperationId<> operationClient.OperationId then yield! IncreaseMajor
if operationAPI.Summary <> operationClient.Summary then yield! IncreaseMajor
}
当生成的数据具有相同的 OperationId、相同的摘要和不同的描述时,测试应该会失败。 但事实并非如此,这促使我创建了自己的生成器或至少尝试这样做:
我希望我的测试是这样写的:
testProperty "calculate Operation against different operations should increase major" <| fun (operationId:ElementSet<string>) (summary:ElementSet<string>) ->
因此我相应地创建了一个类型:
type ElementSet<'a> =
| Same of 'a
| Different
和这种类型的生成器:
let setGen<'a> =
Gen.oneof [
gen {
let! v = Arb.generate<'a>
return Same(v)
}
gen { return Different}
]
type ElementSetGenerator =
static member ElementSet() =
Arb.fromGen setGen<'a>
do Arb.register<ElementSetGenerator>() |> ignore
然后我试图提取数据来构建我的对象:
let createOpenApiOperation operationId summary=
let pi = OpenApiOperation(OperationId=operationId.Get, Summary=summary.Get)
pi
Get 方法尚不存在,所以我准备通过向我的 ElementSet<'a> 添加一个成员来实现它:
type ElementSet<'a> =
| Same of 'a
| Different
with member this.Get =
match this with
| Same s -> s
| Different -> Arb.generate<'a>// some random generation here
这就是我被困的地方。当我提取数据时,我很想在这里获得一些随机性。我想知道这样做是否正确,或者我是否应该早点回答这个问题?
感谢您的意见。
我想我找到了,答案是一开始就处理它:
let setGen<'a when 'a:equality> =
Gen.oneof [
gen {
let! v = Arb.generate<'a>
return Same(v)
}
gen {
let! x,y =
Arb.generate<'a>
|> Gen.two
|> Gen.filter (fun (a,b)-> a <> b)
return Different(x,y)
}
]
然后使用两个 getter 来访问值:
type ElementSet<'a> when 'a:equality=
| Same of 'a
| Different of 'a*'a
with member this.Fst = match this with | Same s -> s | Different (a, b)-> a
member this.Snd = match this with | Same s -> s | Different (a, b)-> b
这样我就可以在我的测试中访问值:
testProperty "calculate Operation against different operations should increase major" <| fun (operationId:ElementSet<NonWhiteSpaceString>) (summary:ElementSet<NonWhiteSpaceString>) (description:ElementSet<NonWhiteSpaceString>) ->
let operationClient = createOpenApiOperation operationId.Fst summary.Fst description.Fst
let operationAPI = createOpenApiOperation operationId.Snd summary.Snd description.Snd
let actual = calculate operationAPI operationClient
Expect.equal actual (Fact.Semver.IncreaseMajor) "return IncreaseMajor"
为了记录,我创建了我的存根,如下所示:
let createOpenApiOperation (operationId:NonWhiteSpaceString) (summary:NonWhiteSpaceString) (description:NonWhiteSpaceString)=
let pi = OpenApiOperation(OperationId=operationId.Get, Summary=summary.Get, Description=description.Get)
pi