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 generating test objects in C# #656

Closed JaggerJo closed 7 months ago

JaggerJo commented 7 months ago

Hi there πŸ‘‹

I'm trying to generate a list of C# records as test data for a property based test, but face a few issues.

1. Nullable reference types

How can I configure the generator to respect nullability of reference types in C#?

FullName is not allowed to be null -> don't generate null values Notes is allowed to be null -> generate null values

public record Customer 
{
    public string FullName { get; init; }
    public string? Notes { get; init }
}

2. Generating complex objects

Let's assume I need to generate random samples of the type below, but there are certain rules on what's valid input.

Example Rules: Name: Any not null string Shorthand: First 3 chars from Name, if Name is shorter then 3 then equal to Name. InternalPhonePrefix: Any random string.

(excuse my silly examples πŸ™ƒ)

public record Department 
{
    public string Name { get; init; }
    public string Shorthand { get; init }
    public string InternalPhonePrefix { get; init; }
}

I guess this would work, but is there a better way?

    public static Arbitrary<Department> RandomDepartment() =>
        Arb.Generate<(string, string, string)>()
            .Select(department => new Department
            {
                Name = department.Item1,
                Shorthand = department.Item2,
                InternalPhonePrefix = department.Item3
            })
            .ToArbitrary();
BennieCopeland commented 7 months ago

For number 2, you can use the query syntax

var generator =
    from name in Arb.Generate<string>()
    from phone in Arb.Generate<string>()
    select new Department
    {
        Name = name,
        Shorthand = name.Substring(0, 2),
        InternalPhonePrefix = phone
    }
JaggerJo commented 7 months ago

For number 2, you can use the query syntax

var generator =
    from name in Arb.Generate<string>()
    from phone in Arb.Generate<string>()
    select new Department
    {
        Name = name,
        Shorthand = name.Substring(0, 2),
        InternalPhonePrefix = phone
    }

Thanks! That's quite handy

BennieCopeland commented 7 months ago

It's also useful if you're using the CSharpFunctionalExtensions nuget package. You can do the same thing with a bunch of Result's or Maybe's. Not as good as a computation expression in F#, but it gets the job done.

kurtschelfthout commented 7 months ago

Nullable reference types

FsCheck cannot currently do this, based on the type alone. From a quick searc, since .NET 6 or 7 we can check the nullability of a type at runtime, so this may be possible to implement via that route.

Generating complex objects

@BennieCopeland's is probably the best way to do that.

JaggerJo commented 7 months ago

Nullable reference types

FsCheck cannot currently do this, based on the type alone. From a quick searc, since .NET 6 or 7 we can check the nullability of a type at runtime, so this may be possible to implement via that route.

Interesting πŸ‘

Generating complex objects

@BennieCopeland's is probably the best way to do that.

Yup, that actually works pretty well.