Expecto FsCheck 在生成字符串时获取堆栈溢出异常
Expecto FsCheck getting stack overflow exception when generating string
我正在尝试学习如何正确使用 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
函数之前就被抛出
我做错了什么?谢谢
您正在尝试使用 FsCheck 的字符串生成器来重新定义其字符串生成器的工作方式,但是当您这样做时,它会递归调用自身,直到用完堆栈 space。这是一个已知问题: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
您正在为类型字符串定义一个新的生成器,但在其中使用 string * string
的生成器,它使用 string
的生成器。不幸的是,FsCheck 似乎将生成器存储在全局可变状态(也许有充分的理由?),我认为这意味着生成器一直调用自身直到堆栈溢出。
您可以通过为自定义包装器类型而不是纯字符串(如下所示)定义生成器来解决此问题。
您将遇到的下一个问题是空引用异常。初始生成的字符串可能是 null
而您尝试访问 .Length
属性。这可以使用 String.length
函数来解决,returns 0
for null
.
经过这些更改,您的生成器看起来像这样:
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(".")
我正在尝试学习如何正确使用 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
函数之前就被抛出
我做错了什么?谢谢
您正在尝试使用 FsCheck 的字符串生成器来重新定义其字符串生成器的工作方式,但是当您这样做时,它会递归调用自身,直到用完堆栈 space。这是一个已知问题: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
您正在为类型字符串定义一个新的生成器,但在其中使用 string * string
的生成器,它使用 string
的生成器。不幸的是,FsCheck 似乎将生成器存储在全局可变状态(也许有充分的理由?),我认为这意味着生成器一直调用自身直到堆栈溢出。
您可以通过为自定义包装器类型而不是纯字符串(如下所示)定义生成器来解决此问题。
您将遇到的下一个问题是空引用异常。初始生成的字符串可能是 null
而您尝试访问 .Length
属性。这可以使用 String.length
函数来解决,returns 0
for null
.
经过这些更改,您的生成器看起来像这样:
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(".")