FsCheck懒生成器

huangapple go评论121阅读模式
英文:

FsCheck lazy generators

问题

我有关于在我的测试中生成数据的问题。

测试属性 "对不同操作计算操作应该增加主要操作" <| 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 "对不同操作计算操作应该增加主要操作" <| fun (operationId:ElementSet) (summary:ElementSet) ->

因此,我相应地创建了一个类型:

type ElementSet<'a> =
| Same of 'a
| Different

并为这种类型创建了一个生成器:

let setGen<'a> =
Gen.oneof [
gen {
let! v = Arb.generate<'a>
return Same(v)
}
gen { return Different}
]

类型 ElementSetGenerator =
static member ElementSet() =
Arb.fromGen setGen<'a>

do Arb.register() |> 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>// 在这里进行一些随机生成

这就是我卡住的地方。我希望在提取数据时获得一些随机性。我想知道这是否是正确的做法,或者我应该更早地解决这个问题?

感谢您的意见。

英文:

I have issues with generation of data within my tests.

    testProperty &quot;calculate Operation against different operations should increase major&quot; &lt;| fun  operationIdApi operationIdClient summaryApi summaryClient descriptionApi descriptionClient   -&gt; 
    ( notAllEqual [
            fun () -&gt; assessEquality &lt;| StringEquals(operationIdApi, operationIdClient)
            fun () -&gt; assessEquality &lt;| StringEquals(summaryApi , summaryClient)
            fun () -&gt; assessEquality &lt;| StringEquals(descriptionApi, descriptionClient)
        ]) ==&gt; lazy (
        let operationClient = createOpenApiOperation operationIdClient summaryClient descriptionClient
        let operationAPI = createOpenApiOperation operationIdApi summaryApi descriptionApi
        let actual = calculate operationAPI operationClient
        Expect.equal actual (Fact.Semver.IncreaseMajor) &quot;return IncreaseMajor&quot;
    )

The code that is actually tested is :

 semver {
            if operationAPI.OperationId&lt;&gt; operationClient.OperationId then yield! IncreaseMajor
            if operationAPI.Summary &lt;&gt; operationClient.Summary then yield! IncreaseMajor
        }

The test should fail when the data produced is same OperationId, same summary and different description.
But it does not and it led me to create my own generator or at least try to do so:

I wanted my test to be written like this :

testProperty &quot;calculate Operation against different operations should increase major&quot; &lt;| fun  (operationId:ElementSet&lt;string&gt;)  (summary:ElementSet&lt;string&gt;)   -&gt; 

Therefore I create a type accordingly:

type ElementSet&lt;&#39;a&gt; =
    | Same of &#39;a
    | Different 

and a generator for this type :

let setGen&lt;&#39;a&gt; =
    Gen.oneof [
            gen { 
                let! v = Arb.generate&lt;&#39;a&gt;
                return Same(v)
            }
            gen { return Different}
    ]

type ElementSetGenerator =
    static member ElementSet() = 
        Arb.fromGen setGen&lt;&#39;a&gt;

do Arb.register&lt;ElementSetGenerator&gt;() |&gt; ignore

I was then trying to extract the data to construct my object :

let createOpenApiOperation operationId summary=
let pi  = OpenApiOperation(OperationId=operationId.Get, Summary=summary.Get)    
pi

The Get method did not exist yet so I was about to implement it by adding a member to my ElementSet<'a>:

type ElementSet&lt;&#39;a&gt; =
    | Same of &#39;a
    | Different 
    with member this.Get = 
        match this with
            | Same s -&gt; s
            | Different -&gt;  Arb.generate&lt;&#39;a&gt;// some random generation here 

And this is where I am stuck. I would love to get some randomness here when I extract data. I wonder if this is the correct way to do so, or if I should have answered the problem earlier?

Thanks for your inputs.

答案1

得分: 0

我认为我找到了答案,答案是在开始处理它:

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
英文:

I think I found it, the answer was to handle it at the beginning :

let setGen&lt;&#39;a when &#39;a:equality&gt;   =
    Gen.oneof [
            gen { 
                let! v = Arb.generate&lt;&#39;a&gt;
                return Same(v)
            }
            gen { 
                let! x,y  =
                    Arb.generate&lt;&#39;a&gt;
                    |&gt; Gen.two
                    |&gt; Gen.filter (fun (a,b)-&gt; a &lt;&gt; b)

                return Different(x,y)
            }
    ]

and then to use two getter to access the values :

type ElementSet&lt;&#39;a&gt; when &#39;a:equality=
    | Same of &#39;a
    | Different of &#39;a*&#39;a
    with member this.Fst =  match this with | Same s -&gt; s | Different (a, b)-&gt;  a
         member this.Snd =  match this with | Same s -&gt; s | Different (a, b)-&gt;  b

this way I can access values within my test:

 testProperty &quot;calculate Operation against different operations should increase major&quot; &lt;| fun  (operationId:ElementSet&lt;NonWhiteSpaceString&gt;)  (summary:ElementSet&lt;NonWhiteSpaceString&gt;) (description:ElementSet&lt;NonWhiteSpaceString&gt;)   -&gt; 
  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) &quot;return IncreaseMajor&quot;

for the record I then have the creation of my stub as follows :

let createOpenApiOperation (operationId:NonWhiteSpaceString) (summary:NonWhiteSpaceString) (description:NonWhiteSpaceString)=
let pi  = OpenApiOperation(OperationId=operationId.Get, Summary=summary.Get, Description=description.Get)    
pi

huangapple
  • 本文由 发表于 2020年1月3日 18:50:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/59577240.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定