考虑一个有区别的联合:
type DU = | Foo of string | Bar of int | Baz of decimal * float | Qux of bool
我想DU
用 FsCheck 创建一个值列表,但我不希望任何值都是这种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 - 它并没有真正filter,而只是产生所需的情况(因此仅通过省略进行过滤)。
- 它不是特别可维护的,因为如果我在 中添加第五个案例
DU
,我必须记住还要Gen
为那个案例添加一个。
有没有更优雅的方法来告诉 FsCheck 过滤掉Qux
值?