考虑一个被歧视的联盟:
type DU = | Foo of string | Bar of int | Baz of decimal * float | Qux of bool
我想使用FsCheck创建DU
值列表,但我不希望这些值都是Qux
的情况。
此谓词已存在:
let isQux = function Qux _ -> true | _ -> false
首次尝试
我首次尝试在没有DU
案例的情况下创建Qux
值列表,如下所示:
type DoesNotWork =
static member DU () = Arb.from<DU> |> Arb.filter (not << isQux)
[<Property(MaxTest = 10 , Arbitrary = [| typeof<DoesNotWork> |])>]
let repro (dus : DU list) =
printfn "%-5b : %O" (dus |> List.exists isQux |> not) dus
运行它似乎会产生堆栈溢出,所以我假设场景背后发生的是Arb.from<DU>
调用DoesNotWork.DU
。
第二次尝试
然后我尝试了这个:
type DoesNotWorkEither =
static member DU () =
Arb.generate<DU>
|> Gen.suchThat (not << isQux)
|> Arb.fromGen
[<Property(MaxTest = 10 , Arbitrary = [| typeof<DoesNotWorkEither> |])>]
let repro (dus : DU list) =
printfn "%-5b : %O" (dus |> List.exists isQux |> not) dus
与上述问题相同。
详细解决方案
这是我迄今为止能够提出的最佳解决方案:
type WithoutQux =
static member DU () =
[
Arb.generate<string> |> Gen.map Foo
Arb.generate<int> |> Gen.map Bar
Arb.generate<decimal * float> |> Gen.map Baz
]
|> Gen.oneof
|> Arb.fromGen
[<Property(MaxTest = 10 , Arbitrary = [| typeof<WithoutQux> |])>]
let repro (dus : DU list) =
printfn "%-5b : %O" (dus |> List.exists isQux |> not) dus
这有效,但有以下缺点:
isQux
功能,所以它似乎巧妙地违反DRY DU
添加第五个案例,我必须记住以便为此添加Gen
情况下。是否有更优雅的方式告诉FsCheck过滤掉Qux
值?
答案 0 :(得分:7)
而不是试图使用已注册的实例类型的Arb.generate
,而不是尝试定义的实例,这会导致无限循环,而是使用Arb.Default.Derive()
直接到反射式发电机。
https://github.com/fscheck/FsCheck/blob/master/src/FsCheck/Arbitrary.fs#L788-788
这是我们应该能够在FsCheck中开箱即用的常见错误:https://github.com/fscheck/FsCheck/issues/109
OP中的特殊问题可以这样解决:
type WithoutQux =
static member DU () = Arb.Default.Derive () |> Arb.filter (not << isQux)
[<Property(MaxTest = 10 , Arbitrary = [| typeof<WithoutQux> |])>]
let repro (dus : DU list) =
printfn "%-5b : %O" (dus |> List.exists isQux |> not) dus
答案 1 :(得分:4)
The below should work:
type DU = | Foo of string | Bar of int | Baz of decimal * float | Qux of bool
let isQux = function Qux _ -> true | _ -> false
let g = Arb.generate<DU> |> Gen.suchThat (not << isQux) |> Gen.listOf
type DoesWork =
static member DU () = Arb.fromGen g
[<Property(MaxTest = 10 , Arbitrary = [| typeof<DoesWork> |])>]
let repro (dus : DU list) =
printfn "%-5b : %O" (dus |> List.exists isQux |> not) dus
Note I used Gen.listOf
at the end - seems like FsCheck fails to generate itself a list with the given generator