【发布时间】:2016-07-19 23:22:06
【问题描述】:
我使用 FsCheck 进行基于属性的测试,因此我为自定义类型定义了一组生成器。一些类型由其他类型组成,并且所有类型都有生成器。为字母数字类型定义了一个生成器后,我想为 RelativeUrl 类型定义一个生成器,RelativeUrl 是由斜线符号分隔的 1-9 个字母数字值的列表。这是有效的定义(Alpanumeric 具有将其转换为字符串的“Value”属性):
static member RelativeUrl() =
Gen.listOfLength (System.Random().Next(1, 10)) <| Generators.Alphanumeric()
|> Gen.map (fun list -> String.Join("/", list |> List.map (fun x -> x.Value)) |> RelativeUrl)
尽管它很简单,但我不喜欢使用 Random.Next 方法而不是使用 FsCheck 随机生成器。所以我试着像这样重新定义它:
static member RelativeUrl_1() =
Arb.generate<byte>
|> Gen.map int
|> Gen.suchThat (fun x -> x > 0 && x <= 10)
|> Gen.map (fun length -> Gen.listOfLength length <| Generators.Alphanumeric())
|> Gen.map (fun list -> String.Join("/", list))
编译器接受它,但实际上它是错误的:最后一条语句中的“列表”不是字母数字值的列表,而是 Gen。下一次尝试:
static member RelativeUrl() =
Arb.generate<byte>
|> Gen.map int
|> Gen.suchThat (fun x -> x > 0 && x <= 10)
|> Gen.map (fun length -> Gen.listOfLength length <| Generators.Alphanumeric())
|> Gen.map (fun list -> list |> Gen.map (fun elem -> String.Join("/", elem |> List.map (fun x -> x.Value)) |> RelativeUrl))
但这也不起作用:我返回的是 RelativeUrl 的 Gen,而不是 RelativeUrl 的 Gen。那么在不同级别组合生成器的正确方法是什么?
【问题讨论】:
-
不能用
System.Random,不能用Gen.choose吗? -
@MarkSeemann,您可以通过立即采样使用
choose而不是Random,但这会破坏目的,因为这个choose不会成为生成的生成器的一部分,但是将作为一种效用函数工作。真的不比Random好。 -
@FyodorSoikin 我不同意——你永远不想在生成器中使用
System.Random,因为它会破坏可重复性(即相同的种子每次返回不同的结果)并使收缩变得不可能(或至少是不确定的) . -
@KurtSchelfthout:Mark 建议使用
Gen.choose而不是 Random。如果在 OP 的第一个代码块中将其插入 Random 的位置,那将毫无用处,不是吗? -
@FyodorSoikin 再次阅读您的评论 - 是的,如果您使用
Gen.choose |> Gen.sample,它并不比 Random 更好。但不要认为这就是@MarkSeemann 的意思:) 到处都是混乱……但我认为我们都在说同样的话。