1

我用一些自定义生成器定义了一个类型,以使 FsCheck 生成一些类型的自定义实例。但对于其中一种复杂类型,我想先使用默认的 FsCheck 生成,然后再调整结果。这是一个(简化的)代码:

type CustomGenerators =
    static member FirstCustomType() = /* function that returns FirstCustomType */
    static member SecondCustomType() =
        Arb.generate<SecondCustomType>
        |> Gen.map (fun x -> /* adjust some data in the generated instance */)
        |> Arb.fromGen

问题是当 SecondCustomType() 静态方法调用 Arb.generate 时,它​​会立即调用 SecondCustomType() 导致无限递归。我知道 Arb.generate 必须尊重自定义生成器,所以这就是它调用静态 SecondCustomType() 的原因,但我需要调用 SecondCustomType 的默认(非自定义)Arb.generate 实现。我不能从不同的类型调用实现,因为我的自定义生成器使用 FirstCustomType 的自定义生成器,因此默认的 SecondCustomType 实现必须知道在 CustomGenerators 类型中定义的所有自定义生成器。这是一个糟糕的圈子,我还没有找到一个干净的解决方案(只有解决方法)。

4

1 回答 1

5

所有“默认”(即开箱即用)生成器都在FsCheck.Arb.Default类中。根据您的SecondCustomType实际情况,您可以使用该类的一些方法,例如BoolString

如果您的类型是正确的代数 F# 类型(即联合、记录或元组),您可以利用由Default.Derive.

type CustomGenerators =
    static member SecondCustomType() =
        Arb.Default.Derive<SecondCustomType>()
        |> Arb.toGen
        |> Gen.map (fun x -> (* adjust some data in the generated instance *) )
        |> Arb.fromGen

话虽如此,我同意上面 Mark 的评论:使用这些静态方法填充器用于类型类生成器总是有点尴尬。就像 Mark 一样,我更喜欢让 FsCheck 提供开箱即用的功能,然后使用常规函数组合所需的输入。我给你举个例子。

考虑这种类型,可能无法由 FsCheck 开箱即用地生成:

type SomeAwkwardType( name: string, id: int, flag: bool ) =
   member this.Name = name
   member this.Id = id
   member this.Flag = flag

这是使用 static-shim-for-type-class 生成器的尴尬方式:

type AwkwardTypeGenerator() =
   static member Gen() =
      gen {
         let! name = Arb.generate<string>
         let! id = Arb.generate<int>
         let! flag = Arb.generate<bool>
         return SomeAwkwardType( name, id, flag )
      }

module Tests =
   let [Property] ``Test using the awkward generator`` (input: SomeAwkwardType) = 
      someFn input = 42

这是生成输入的更直接(在我看来)的方式:

module Tests =
   let [Property] ``Test using straightforward generation`` (name, id, flag) = 
      let input = SomeAwkwardType( name, id, flag )
      someFn input = 42

这不仅更短、更简洁,而且还具有一年后不用费力寻找实现生成器的静态类的代码库的优势。

于 2016-04-01T14:29:51.283 回答