Expecto FsCheck on exception when generating row

I am trying to learn how to use FsCheck correctly and integrate it with Expecto at the moment. I can run property tests if I use the default FsCheck configuration, but when I try to use my own generator it throws an exception.

Here is my generator

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

      

And I'm trying to use it like this:

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

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

testPropertyWithConfig config "Must end with period" propertyTests

      

The exception is thrown before it even hits the function Gen.where

What am I doing wrong? Thanks to

+3


source to share


2 answers


You are trying to use the FsCheck string generator to override how its string generator works, but when you do, it will recursively call itself until it runs out of stack space. This is a known issue: https://github.com/fscheck/FsCheck/issues/109

Does this alternative help?



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

      

+5


source


You are defining a new generator for a string of type, but inside that you are using a generator for string * string

which uses a generator for string

. FsCheck, unfortunately, seems to keep generators in a globally mutable state (perhaps with good reason?), And I think that means that the generator keeps calling itself until.

This can be solved by specifying a generator for a custom wrapper type instead of a simple string (shown below).

The next problem you will run into will be a blank exception reference. The initial generated string might be null

and you are trying to access the property .Length

. This can be solved by using instead a function String.length

that returns 0

for null

.

With these changes, your generator looks like this:



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)

      

And your property needs a little modification:

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

      

+3


source







All Articles