F# 如何在FsCheck中轻松筛选出歧视性工会案例?
以一个受歧视的工会为例:F# 如何在FsCheck中轻松筛选出歧视性工会案例?,f#,fscheck,F#,Fscheck,以一个受歧视的工会为例: 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 =
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
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
与上述问题相同
详细解决方案
这是迄今为止我能想到的最好的解决方案:
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
不带qux的类型=
静态成员DU()=
[
Arb.generate |>Gen.map Foo
Arb.generate |>Gen.map条
Arb.generate |>Gen.map Baz
]
|>第一将军
|>弗罗姆根
[]
让我们重新开始(DU:DU列表)=
printfn“%-5b:%O”(dus |>List.exists isQux |>not)dus
这是可行的,但有以下缺点:
- 好像要做很多工作
- 它没有使用已经可用的
函数,因此它似乎微妙地违反了DRYisQux
- 它并不真正过滤,而是只生成所需的情况(因此只通过省略过滤)
- 它不是特别可维护的,因为如果我在
中添加第五个案例,我必须记住也要为该案例添加一个DU
Gen
Qux
值?以下方法应该有效:
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
type DU=|Foo of string | Bar of int | Baz of decimal*float | Qux of bool
设isQux=函数Qux u->真|->假
设g=Arb.generate |>Gen.suchThat(非Gen.listOf
多斯沃克型=
静态成员DU()=Arb.fromGen
[]
让我们重新开始(DU:DU列表)=
printfn“%-5b:%O”(dus |>List.exists isQux |>not)dus
注意:我在结尾使用了
Gen.listOf
——似乎FsCheck无法使用给定的生成器而不是Arb.generate
,它尝试将注册实例用于类型,这是您试图定义的实例,这会导致无限循环,请使用Arb.Default.deriver()
将直接进入基于反射的生成器
这是一个常见的错误,我们应该能够在FsCheck中立即解决:
OP中的特定问题可以这样解决:
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
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
不带qux的类型=
静态成员DU()=Arb.Default.derivate()|>Arb.filter(not List.exists isQux |>not)DU
好吧,通过创建一个任意的
而不是一个任意的
来规避这个问题,这可能解决了我眼前的问题,但似乎并没有解决根本的问题。这难道不是一个问题,我们没有得到DU?+1对Baz的提醒和对Qux的介绍的缩减支持吗即使没有这些,这也是个好问题。