1

In my previous question Kurt pointed me to this code of FsCheck about setting the Arbitrary type.

I have the following Arbitrary (disclaimer: I have no idea what I am doing..., still finding FsCheck notoriously hard to understand but I'm dead set on getting it to work), which in itself is a simplified version of something I created earlier:

type MyArb() =
    inherit Arbitrary<DoNotSize<int64>>()
        override x.Generator = Arb.Default.DoNotSizeInt64().Generator

And I use it as instructed:

[<Property(Verbose = true, Arbitrary= [| typeof<MyArb> |])>]
static member  MultiplyIdentity (x: int64) = x * 1L = x

This gives me a (somewhat hopeful) error message that I'm missing something:

 System.Reflection.TargetInvocationException : Exception has been thrown by the target of an invocation.
  ----> System.Exception : No instances found on type Tests.Arithmetic.MyArb. Check that the type is public and has public static members with the right signature.
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   at System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at FsCheck.Runner.checkMethod(Config config, MethodInfo m, FSharpOption`1 target) in C:\Users\Kurt\Projects\FsCheck\FsCheck\src\FsCheck\Runner.fs:line 318
   at FsCheck.NUnit.Addin.FsCheckTestMethod.runTestMethod(TestResult testResult) in C:\Users\Kurt\Projects\FsCheck\FsCheck\src\FsCheck.NUnit.Addin\FsCheckTestMethod.fs:line 100

Looking back at that Github code I see two Atrbitrary classes but neither with any inheritance and they both have different static members.

How can I create a random-number generator and assign it as an Arbitrary statically to my NUnit tests?

Community
  • 1
  • 1
Abel
  • 56,041
  • 24
  • 146
  • 247

3 Answers3

4

The type you provide in the Property.Arbitrary parameter should have static members (possibly several) of type Arb. As in the code you linked:

type TestArbitrary2 =
   static member NegativeDouble() =
       Arb.Default.Float()
       |> Arb.mapFilter (abs >> ((-) 0.0)) (fun t -> t <= 0.0)

Applying this to your code, it should look like this:

 type MyArb() =
    static member m() = Arb.Default.DoNotSizeInt64()

The meaning of the Property.Arbitrary parameter is not "an implementation of Arbitrary", but rather "a bucket of typeclass implementations".

You see, the original Haskell implementation of QuickCheck relies on typeclasses to provide values of different types. In order for a particular type to be "quick-checkable", there needs to be an instance of the 'Arbitrary' class defined for that type (for example, here are instances for all basic types).

Since F# doesn't support type classes as such, FsCheck has to fake it, and this is the scheme used there: each type class instance is represented by a static member that returns the function table. For example, if we wanted to simulate the Eq typeclass, we'd define it something like this:

type Eq<'a> = { eq: 'a -> 'a -> bool; neq: 'a -> 'a -> bool }

type EqInstances() =
   static member ForInt() : Eq<int> = 
      { eq = (=); neq = (<>) }

   static member ForMyCustomType() : Eq<MyCustomType> = 
      { eq = fun a b -> a.CompareTo(b) = 0
        neq = fun a b -> a.CompareTo(b) <> 0 }

But because you can't just scan all static member in all loaded assemblies (that would be prohibitively expensive), there is this little inconvenience of providing the type explicitly (as a bonus, it allows to control the visibility of "instances").

Fyodor Soikin
  • 78,590
  • 9
  • 125
  • 172
  • So simple? So no inheritance, attributes etc? Trying... _(edit: tried, works instantly, thanks!)_ – Abel Dec 06 '16 at 16:51
  • Thanks for the additional explanation, it makes sense. However, `static member m()` is never called (I tried with a `printfn` statement just to be sure). This may well be an FsCheck bug (more likely my bug ;), but as written above, it never hits. – Abel Dec 06 '16 at 17:07
  • I figured it out. I needed to unwrap the `DoNotSize` type, it wasn't recognized as the requested type for the function under scrutiny. I.e., the definition now being `Arb.Default.DoNotSizeInt64().Generator |> Gen.map DoNotSize.Unwrap |> Arb.fromGen`. – Abel Dec 06 '16 at 17:30
2

This question demonstrates clearly, IMO, why the Reflection-based API for FsCheck is less than ideal. I tend to avoid that API completely, so I'd instead write the OP property like this:

open FsCheck
open FsCheck.Xunit

[<Property>]
let MultiplyIdentity () =
    Arb.Default.DoNotSizeInt64 () |> Prop.forAll <| fun (DoNotSize x) -> x * 1L = x

As the open directives suggest, this uses FsCheck.Xunit instead of FsCheck.NUnit, but AFAIK, there's no difference in the way the API works.

The advantage of this approach is that it's type-safe and more lightweight, because you don't have to implement static classes every time you need to tweak FsCheck.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • I like it. Just tried it. I was afraid that the test being `unit`, that the verbosity wouldn't work (but it does and I see the random int64's). While I'm still curious as to how to solve the original problem (Fyodor made it runnable, but the method isn't called), this looks like a better approach from several perspectives, thanks. – Abel Dec 06 '16 at 17:24
  • As an aside, and interestingly, only some 5 of the 100 tests use a positive `int64` with this code, rest is negative. This holds for several runs. Is that by design? (btw, original problem solved, I forgot to unwrap, your type-safe hint helped) – Abel Dec 06 '16 at 17:26
  • @Abel Now that you ask, that generator does, indeed, look like it's skewed. I don't know why that is, but I've created an issue asking about that: https://github.com/fscheck/FsCheck/issues/332 – Mark Seemann Dec 06 '16 at 18:35
  • +1 I also prefer this approach, which is also closer to all the other QuickCheck-based clones. You can even get rid of the `[]` attribute and use the functions in the `Check` module inside the test. `[]` is all xUnit.net needs to discover the property-based test, then. :) – Nikos Baxevanis Dec 07 '16 at 09:34
  • @NikosBaxevanis Doesn't that require you to pipe the entire property into `Check.QuickThrowOnFailure`? Last time I tried that approach, I found it somewhat awkward, but it's been a while... Consider adding your alternative as an answer here ;) – Mark Seemann Dec 07 '16 at 14:06
  • @MarkSeemann Yes, it does require to pipe the entire property into `Check.QuickXyz`. – Nikos Baxevanis Dec 08 '16 at 04:05
  • @MarkSeemann Piping into `Check.QuickXyz` makes things more explicit. Not a bad idea, I think. – Nikos Baxevanis Dec 09 '16 at 14:40
  • This seems to e the perfect blend for NUnit + FsCheck. Without the addin, it will get too slow to handle, with the addin (through `PropertyAttribute`) and this approach, I seem to have the best of both worlds (type-safety is a strong argument). And I still get my stdout logging. – Abel Dec 09 '16 at 15:27
2

If you prefer the approach described by Mark Seemann, then you may also consider using plain-FsCheck and get rid of FsCheck.Xunit entirely:

module Tests

open FsCheck

let [<Xunit.Fact>] ``Multiply Identity (passing)`` () = 
    Arb.Default.DoNotSizeInt64 ()
    |> Prop.forAll
    <| fun (DoNotSize x) ->
        x * 1L = x
    |> Check.QuickThrowOnFailure

let [<Xunit.Fact>] ``Multiply Identity (failing)`` () = 
    Arb.Default.DoNotSizeInt64 ()
    |> Prop.forAll
    <| fun (DoNotSize x) ->
        x * 1L = -1L |@ sprintf "(%A should equal %A)" (x * 1L) x
    |> Check.QuickThrowOnFailure

xUnit.net testrunner output:

------ Test started: Assembly: Library1.dll ------

Test 'Tests.Multiply Identity (failing)' failed: System.Exception:
    Falsifiable, after 1 test (2 shrinks) (StdGen (2100552947,296238694)):

Label of failing property: (0L should equal 0L)
Original:
DoNotSize -23143L
Shrunk:
DoNotSize 0L

    at <StartupCode$FsCheck>.$Runner.get_throwingRunner@365-1.Invoke(String me..
    at <StartupCode$FsCheck>.$Runner.get_throwingRunner@355.FsCheck-IRunner-On..
    at FsCheck.Runner.check[a](Config config, a p)
    at FsCheck.Check.QuickThrowOnFailure[Testable](Testable property)
    C:\Users\Nikos\Desktop\Library1\Library1\Library1.fs(15,0): at Tests.Multi..

1 passed, 1 failed, 0 skipped, took 0.82 seconds (xUnit.net 2.1.0 build 3179).
Community
  • 1
  • 1
Nikos Baxevanis
  • 10,868
  • 2
  • 46
  • 80
  • I actually had an NUnit + FsCheck running, but wanted the combination with `Property` to make it more easily configurable and to get more meaningful output in my test runner _(OT: and xUnit is not useful for my scenario, it doesn't do out-of-the box stdout and stderr writing, claims it's in the way of multi-threading (it is), while that is how I run NUnit, with stdout, with multithreading, using NCrunch)_. – Abel Dec 09 '16 at 15:20
  • And as another aside, the piping approach had a strange side effect in the NUnit runner (only the standard one from NUnit, not NCrunch): it reports it runs a test in 0.12s, but returns only after 10+ seconds (or longer, it starts to be noticeable from 1000 or so FsCheck runs with the most trivial function). I have some idea what the cause is, and this cause is fixed in FsCheck's NUnit addin (and I didn't find a way to mold that fix into the vanilla way you show above with xUnit). Hence again the requirement for wanting to use `PropertyAttribute`. – Abel Dec 09 '16 at 15:23