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