【发布时间】:2015-06-26 08:30:48
【问题描述】:
考虑一个有区别的联盟:
type DU = | Foo of string | Bar of int | Baz of decimal * float | Qux of bool
我想使用 FsCheck 创建一个 DU 值列表,但我不希望任何值都属于 Qux 大小写。
这个谓词已经存在:
let isQux = function Qux _ -> true | _ -> false
第一次尝试
我第一次尝试在没有Qux 的情况下创建DU 值列表是这样的:
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 值?
【问题讨论】:
-
+1 用于提醒有关 Baz 和 Qux 的介绍。尽管即使没有这些,这也是一个很好的问题。