3

I'm writing an FSCheck generator to create strings having the following properties:

  • They are non-null
  • Trimming them won't affect the length
  • They contain no spaces.

Here's my generator code:

namespace Example

open FsCheck.Arb

module public Generation = 

    let hasChars (s : string) =
        (isNull s |> not)
        && s.Length > 0

    let isTrimmed (s : string) =
        s.Trim().Length = s.Length

    let isContinuous (s : string) =
        s
        |> Seq.exists ((=) ' ')
        |> not

    [<AbstractClass; Sealed>]
    type public Generators = class end

    type public ContinuousString = ContinuousString of string with
        member x.Get = match x with ContinuousString r -> r
        override x.ToString() = x.Get

    type public Generators with

        static member ContinuousString() =
            Default.String()
            |> filter hasChars
            |> filter isTrimmed
            |> filter isContinuous
            |> convert ContinuousString string

And here's a test intended to verify the generation:

[<Property(Arbitrary=[| typeof<ContinuousString> |], MaxTest=2)>]
let ``A continuous string contains no spaces`` (s: ContinuousString) =
    s.Get.Contains " " |> not

When I run this test, I get:

System.Exception: No instances found on type Example.Generation+ContinuousString. Check that the type is public and has public static members with the right signature.

As far as I can tell looking at the FSCheck source code, the member I have defined should be found by the discovery filter, and the method seems analogous to similar built-in ones such as NonEmptyString.

What have I missed? Thanks!

Kit
  • 2,089
  • 1
  • 11
  • 23
  • 1
    I think `typeof` should be `typeof`. Also, I don't think you need to define it twice: the `type public Generators = class end` definition is doing nothing for you, AFAIK. – rmunn Apr 17 '17 at 15:01
  • Also: `isNull s |> not` could be just `s <> null`. Also: `ContinuousString.Get` is a property, but named like a method. Also: you don't actually need `Get`, you could define the test function's parameter as `(ContinuousString s)` instead of `(s: ContinuousString)`. – Fyodor Soikin Apr 17 '17 at 15:03
  • Great, thanks @rmunn! The corrected `typeof<>` fixed it. The empty type was just there so I was replicating what FSCheck's own code as closely as possible - I didn't expect it to have an effect. If you want to transfer your comment to an answer I'll mark as accepted. Thanks again! – Kit Apr 17 '17 at 15:29
  • Okay, done. Looks better as a real answer anyway, but a comment was the fastest way to get you the answer. – rmunn Apr 17 '17 at 15:46

1 Answers1

5

You're passing FsCheck the wrong type. You should be passing it your Generators class, not your ContinuousString DU. I.e., this:

[<Property(Arbitrary=[| typeof<ContinuousString> |], MaxTest=2)>]
let ``A continuous string contains no spaces`` (s: ContinuousString) =
    s.Get.Contains " " |> not

should have been:

[<Property(Arbitrary=[| typeof<Generators> |], MaxTest=2)>]
let ``A continuous string contains no spaces`` (s: ContinuousString) =
    s.Get.Contains " " |> not

The FsCheck error message was trying to tell you this as well:

Check that the type is public and has public static members with the right signature.

The type that you created that matches what it's looking for is Generators.

rmunn
  • 34,942
  • 10
  • 74
  • 105