fscheck / FsCheck

Random Testing for .NET
https://fscheck.github.io/FsCheck/
BSD 3-Clause "New" or "Revised" License
1.17k stars 156 forks source link

FsCheck not registering a static method that takes Arbitrary<TType> in C# #640

Closed BennieCopeland closed 7 months ago

BennieCopeland commented 1 year ago

When using FsCheck in a C# project, I wanted to register the Maybe type from CSharpFunctionalExtensions. I followed the F# example code for creating a Box<> type, but when it came to registering the generator and shrinker, FsCheck is ignoring the static method. I receive an exception "No instances found on type Maybe. Check that the type is public and has public static members with the right signature.

using FsCheck;

namespace FooBar;

public class Foo<TBar>
{
    public Foo(TBar bar)
    {
        Bar = bar;
    }

    public TBar Bar { get; }
}

public class FooGenerator<TBar>
{
    public static Gen<Foo<TBar>> Generator(Arbitrary<TBar> contents)
    {
        return contents.Generator.Select(v => new Foo<TBar>(v));
    }

    public static Func<Foo<TBar>, IEnumerable<Foo<TBar>>> Shrinker(Arbitrary<TBar> contents)
    {
        return foo => contents.Shrinker(foo.Bar).Select(v => new Foo<TBar>(v));
    }
}

public static class MyGenerators
{
    public static Arbitrary<Foo<TBar>> ArbitraryFoo<TBar>(Arbitrary<TBar> contents)
    {
        return Arb.From(
            FooGenerator<TBar>.Generator(contents),
            FooGenerator<TBar>.Shrinker(contents)
        );
    }
}

public class MyPropertyAttribute : PropertyAttribute
{
    public MyPropertyAttribute()
    {
        Arbitrary = new[] { typeof(MyGenerators) };
    }
}

public class MyTests
{
    [MyProperty]
    public void FooTest(Foo<string> foo) // throws exception
    {

    }
}

If I change the generator registration to remove the Arbitrary<TBar> and move it inside the method block, it works.

public static class MyGenerators
{
    public static Arbitrary<Foo<TBar>> ArbitraryFoo<TBar>()
    {
        var contents = Arb.From<TBar>();
        return Arb.From(
            FooGenerator<TBar>.Generator(contents),
            FooGenerator<TBar>.Shrinker(contents)
        );
    }
}
kurtschelfthout commented 1 year ago

Which version?On 25 Aug 2023, at 04:44, Bennie Copeland @.***> wrote: When using FsCheck in a C# project, I wanted to register the Maybe type from CSharpFunctionalExtensions. I followed the F# example code for creating a Box<> type, but when it came to registering the generator and shrinker, FsCheck is ignoring the static method. I receive an exception "No instances found on type Maybe. Check that the type is public and has public static members with the right signature. using FsCheck;

namespace FooBar;

public class Foo { public Foo(TBar bar) { Bar = bar; }

public TBar Bar { get; }

}

public class FooGenerator { public static Gen<Foo> Generator(Arbitrary contents) { return contents.Generator.Select(v => new Foo(v)); }

public static Func<Foo<TBar>, IEnumerable<Foo<TBar>>> Shrinker(Arbitrary<TBar> contents)
{
    return foo => contents.Shrinker(foo.Bar).Select(v => new Foo<TBar>(v));
}

}

public static class MyGenerators { public static Arbitrary<Foo> ArbitraryFoo(Arbitrary contents) { return Arb.From( FooGenerator.Generator(contents), FooGenerator.Shrinker(contents) ); } }

public class MyPropertyAttribute : PropertyAttribute { public MyPropertyAttribute() { Arbitrary = new[] { typeof(MyGenerators) }; } }

public class MyTests { [MyProperty] public void FooTest(Foo foo) // throws exception {

}

} If I change the generator registration to remove the Arbitrary and move it inside the method block, it works. public static class MyGenerators { public static Arbitrary<Foo> ArbitraryFoo() { var contents = Arb.From(); return Arb.From( FooGenerator.Generator(contents), FooGenerator.Shrinker(contents) ); } }

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you are subscribed to this thread.Message ID: @.***>

BennieCopeland commented 1 year ago

Dotnet Core 7 XUnit test project with FsCheck and FsCheck.Xunit at version 2.16.6

kurtschelfthout commented 1 year ago

Arbitrary methods with Arbitrary arguments are only supported in 3.x if I remember correctly.On 25 Aug 2023, at 09:18, Bennie Copeland @.***> wrote: Dotnet Core 7 XUnit test project with FsCheck and FsCheck.Xunit at version 2.16.6

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you commented.Message ID: @.***>

BennieCopeland commented 1 year ago

According to the Docs it was supported as of three years ago. I'll check and see if it works in F#. But I also see you're working on 3.0 again, which may make this whole issue moot anyway.

BennieCopeland commented 1 year ago

So I checked in F#, and it works just like the docs says it should, so seems like a C# only issue. My workaround works, so if you prefer to close this as "won't fix" to prioritize 3.0, I won't be upset about it.

kurtschelfthout commented 1 year ago

Those docs are for version 3.x though. Suspect you just happened to use 3.x for your F# test. This is all done via reflection, so not expecting a C# vs F# difference.

kurtschelfthout commented 7 months ago

This should all work in 3.x...with the usual caveat of "docs updates needed"