Как переопределить генерацию строки для всех свойств в FsCheck - PullRequest
2 голосов
/ 21 мая 2019

У меня есть куча типов со строками повсюду.Я хочу написать тесты свойств для функций, работающих с этими типами, используя 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 = "\026z^k";} (At least one control character has been escaped as a char code, e.g. \023)
Shrunk:
{LeftLanguageName = "
";
 RightLanguageName = "";}

Так что, очевидно, яя что-то упустил, но я понятия не имел, что.

Я смотрел на

Как генерировать нулевые строки для тестов FsCheck

и

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

, но ни один из них не пытается переопределить генерацию строки глобально (в пределах видимости).

1 Ответ

3 голосов
/ 22 мая 2019

Мне не удалось воспроизвести проблему.Используя FSCheck 2.14.0, я попытался запустить следующее в файле F # Script:

#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 в качестве вашей длины, а затем в цикле сгенерировать символ из набора разрешенных символов и затем объединить эти символы (что дает вам только допустимые строки).

...