nickdodd79 / AutoBogus

A C# library complementing the Bogus generator by adding auto creation and population capabilities.
MIT License
431 stars 50 forks source link

Unable to override structs #45

Closed spring1975 closed 3 years ago

spring1975 commented 3 years ago

I'm actually trying to create bogus data for classes that have NodaType properties. For example.

public class SampleItem
{
    public string Whatever;
    public LocalDate CreationDate;
}

Here's the setup:

AutoFaker.Configure(builder =>
{
    builder
        .WithOverride(new LocalDateOverride())
        ;
});

Faker<SampleItem> sampleItemFaker = new AutoFaker<SampleItem>()
    .RuleFor(si => si.Whatever, f => f.Lorem.Word())
    ;

SampleItem actual = sampleItemFaker
    .UseSeed(TestingConstants.GlobalSeed)
    .Generate();

And the override

class LocalDateOverride : AutoGeneratorOverride
{
    public override bool CanOverride(AutoGenerateContext context)
    {
        return context.GenerateType == typeof(LocalDate);
    }

    public override void Generate(AutoGenerateOverrideContext context)
    {
        // Never even reaches this method 
        var date = (LocalDate)context.Instance;
        date = LocalDate.FromDateTime(context.Faker.Date.Future());
    }
}

Running all of this produces an error as though AutoBogus is trying to create the struct LocalDate with random numbers instead of using the override.

System.ArgumentOutOfRangeException : Value should be in range [-9998-9999] (Parameter 'year')
Actual value was -448584298.

Is there a different way to override structs?

spring1975 commented 3 years ago

Of course, I was not expecting this line to really do anything, date = LocalDate.FromDateTime(context.Faker.Date.Future()); But I just put it in there to illustrate how I'd set the struct.

nickdodd79 commented 3 years ago

Hey @spring1975

You will need to reassign the new value back to the Instance property.

context.Instance = LocalDate.FromDateTime(context.Faker.Date.Future());

Nick.

rspringAltsrc commented 3 years ago

I'm pretty sure that was actually my first try, but I'll give it a go again tomorrow and let you know

spring1975 commented 3 years ago

OK, here's something a little more straightforward as a test

    public class ExampleStructGenerator
    {
        private readonly ITestOutputHelper _outputHelper;

        public ExampleStructGenerator(ITestOutputHelper outputHelper)
        {
            _outputHelper = outputHelper;
        }

        [Fact]
        public void Generate_ExampleStruct()
        {
            IAutoFaker faker = AutoFaker.Create(builder =>
            {
                builder
                    .WithOverride(new ExampleStructOverride())
                    ;
            });

            var exampleStruct = faker.Generate<ExampleStruct>();

            _outputHelper.WriteLine(exampleStruct.Month.ToString());

            Assert.True(exampleStruct.Month > 0 && exampleStruct.Month <= 12);

        }

    }

    class ExampleStructOverride : AutoGeneratorOverride
    {
        public override bool CanOverride(AutoGenerateContext context)
        {
            if (context.GenerateType == typeof(ExampleStruct))
            {
                return true;
            }
            return false;
        }

        public override void Generate(AutoGenerateOverrideContext context)
        {
            // TODO: Despite CanOverride evaluating true, this is not getting here for some reason.
            context.Instance = new ExampleStruct(5);
        }
    }

    public struct ExampleStruct
    {
        public ExampleStruct(int month)
        {
            if (month < 1 || month > 12)
            {
                throw new ArgumentOutOfRangeException(
                    nameof(month),
                    $"Value should be in range [1-12]\nActual value was {month}."
                );
            }

            Month = month;
        }

        public int Month { get; }
    }

I can put a break point on the return true to see it find the right type. However, a breakpoint on where it should be trying to set the context.Instance is never reached. Instead, I get the ArgumentOutOfRangeException with other faked int values.

spring1975 commented 3 years ago

I have found for objects with properties being a NodaTime type, I can do a WithSkip of said property. Then the override works as expected as do the RuleFor definitions.

nickdodd79 commented 3 years ago

Hey @spring1975

Thanks for the updates, sounds like you found a workaround for your issue. I do however, find it strange that the override works with a WithSkip set. I am gonna look more into based on your test example.

Thanks, Nick.

nickdodd79 commented 3 years ago

Hey @spring1975

I have just released v2.12.0 which I hope addresses your issues.

Nick.