【问题标题】:Expecto FsCheck getting stack overflow exception when generating stringExpecto FsCheck在生成字符串时出现堆栈溢出异常
【发布时间】:2017-12-01 15:21:19
【问题描述】:

目前我正在尝试学习如何正确使用 FsCheck,并将其与 Expecto 集成。如果我使用默认的 FsCheck 配置,我可以运行属性测试,但是当我尝试使用自己的生成器时,它会导致堆栈溢出异常。

这是我的发电机

type NameGen() =
    static member Name() =
        Arb.generate<string * string>
        |> Gen.where (fun (firstName, lastName) ->
            firstName.Length > 0 && lastName.Length > 0
        )
        |> Gen.map (fun (first, last) -> sprintf "%s %s" first last)
        |> Arb.fromGen
        |> Arb.convert string id

我正在尝试这样使用它:

let config = { FsCheckConfig.defaultConfig with arbitrary = [typeof<NameGen>] }

let propertyTests input =
    let output = toInitials input
    output.EndsWith(".")

testPropertyWithConfig config "Must end with period" propertyTests

异常在进入Gen.where函数之前就被抛出

我做错了什么?谢谢

【问题讨论】:

    标签: f# fscheck property-based-testing expecto


    【解决方案1】:

    您正在尝试使用 FsCheck 的字符串生成器来重新定义其字符串生成器的工作方式,但是当您这样做时,它将递归调用自身,直到堆栈空间用完。这是一个已知问题:https://github.com/fscheck/FsCheck/issues/109

    这种替代方法有效吗?

    type NameGen =
        static member Name () =
            Arb.Default.NonEmptyString().Generator
            |> Gen.map (fun (NonEmptyString s) -> s)
            |> Gen.two
            |> Gen.map (fun (first, last) -> sprintf "%s %s" first last)
            |> Arb.fromGen
    

    【讨论】:

      【解决方案2】:

      您正在为类型字符串定义一个新的生成器,但在其中您使用的是string * string 的生成器,它使用string 的生成器。不幸的是,FsCheck 似乎将生成器存储在全局可变状态(也许有充分的理由?),我认为这意味着生成器会一直调用自己,直到堆栈溢出。

      您可以通过为自定义包装器类型而不是纯字符串定义生成器来解决此问题(如下所示)。

      您将遇到的下一个问题将是空引用异常。初始生成的字符串可能是null,您尝试访问.Length 属性。这可以改用String.length 函数来解决,它为null 返回0

      通过这些更改,您的生成器将如下所示:

      type Name = Name of string
      
      type NameGen() =
          static member Name() =
              Arb.generate<string * string>
              |> Gen.where (fun (firstName, lastName) ->
                  String.length firstName > 0 && String.length lastName > 0
              )
              |> Gen.map (fun (first, last) -> sprintf "%s %s" first last)
              |> Arb.fromGen
              |> Arb.convert Name (fun (Name n) -> n)
      

      您的属性需要稍作修改:

      let propertyTests (Name input) =
          let output = toInitials input
          output.EndsWith(".")
      

      【讨论】:

        猜你喜欢
        • 2010-11-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-02
        相关资源
        最近更新 更多