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

时间:2019-05-21 20:12:28

标签: f# fscheck

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

我尝试过:

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 = "\026z^k";} (At least one control character has been escaped as a char code, e.g. \023)
Shrunk:
{LeftLanguageName = "
";
 RightLanguageName = "";}

所以很明显我缺少了一些东西,但是我不知道是什么。

我看着

How to generate null strings for FsCheck tests

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

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

1 个答案:

答案 0 :(得分:2)

我无法重现该问题。使用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值作为长度,然后在循环中从允许的字符集中生成一个字符,然后进行串联这些字符(只为您提供 有效字符串)。