如何覆盖 FsCheck 中所有属性的字符串生成

How to override string generation for all properties in FsCheck

我有一堆到处都是字符串的类型。我想使用 FsCheck 为在这些类型上运行的函数编写 属性 测试。对于所有这些,我知道我永远不会得到空字符串或非字母数字字符串。 因此,我想相应地限制字符串的值生成。

我试过这个:

let charSet= "abc" // simplified for the example
let isValidString (s:string) = null<>s && not (s |> Seq.exists (fun c -> not (charSet |> Seq.contains c)))
let createConfig left right = {LeftLanguageName= left; RightLanguageName= right}
let generateString= Arb.generate<string> |> Gen.filter isValidString
let generateConfig= createConfig <!> generateString <*> generateString 

type Generators =
  static member String() =
    { 
        new Arbitrary<string>() with
            override x.Generator = generateString
    }
  static member LanguageConfiguration()= 
    {
        new Arbitrary<LanguageConfiguration>() with
           override x.Generator = generateConfig
    }

Arb.register<Generators>() |> ignore

但是,FsCheck 一直在生成这样的值

Falsifiable, after 14 tests (9 shrinks) (StdGen (2144073619,296598634)):
Original:
{LeftLanguageName = " 1c\J
";
 RightLanguageName = "6z^k";} (At least one control character has been escaped as a char code, e.g. 3)
Shrunk:
{LeftLanguageName = "
";
 RightLanguageName = "";}

很明显我遗漏了什么,但我不知道是什么。

我看了

How to generate null strings for FsCheck tests

http://blog.nikosbaxevanis.com/2015/09/25/regex-constrained-strings-with-fscheck/

但它们都没有尝试覆盖字符串生成全局(在范围内)。

我无法重现该问题。使用 FSCheck 2.14.0,我在 F# 脚本文件中尝试 运行 以下内容:

#r "C:/Temp/nuget/packages/fscheck/lib/net452/FsCheck.dll"
open FsCheck

type LanguageConfiguration = 
  { LeftLanguageName:string; RightLanguageName:string }

let charSet= "abc" // simplified for the example
let isValidString (s:string) = 
  null<>s && not (s |> Seq.exists (fun c -> not (charSet |> Seq.contains c)))
let createConfig left right = {LeftLanguageName= left; RightLanguageName= right}
let generateString= Arb.generate<string> |> Gen.filter isValidString
let generateConfig= createConfig <!> generateString <*> generateString 

type Generators =
  static member String() =
    { new Arbitrary<string>() with
            override x.Generator = generateString }
  static member LanguageConfiguration()= 
    { new Arbitrary<LanguageConfiguration>() with
           override x.Generator = generateConfig }

Arb.register<Generators>() |> ignore

Check.QuickThrowOnFailure (fun (n:LanguageConfiguration) ->
  printfn "%A" n
  true)

这运行了 100 个测试,输出如下所示:

{LeftLanguageName = "";
 RightLanguageName = "";}
{LeftLanguageName = "";
 RightLanguageName = "";}
{LeftLanguageName = "";
 RightLanguageName = "";}
{LeftLanguageName = "";
 RightLanguageName = "";}
{LeftLanguageName = "";
 RightLanguageName = "";}

这表明您的测试生成存在另一个问题(可能只是在这个简化的演示中),即您的 Gen.filter isValidString 调用基本上消除了所有有趣的输入,因为它们无效。

您可以通过以不同方式生成字符串来解决此问题 - 更好的方法是生成一个 int 值作为您的长度,然后在循环中从允许的字符集中生成一个字符,然后然后连接这些字符(这给你 只有 有效字符串)。