Search code examples
c#.net-coredynamicmockingbogus

Mock dynamic properties in expression, for use with Bogus


I want to configure a Bogus rule that sets a dynamic property on an otherwise straight forward model.

N.B. I'm aware this is very much related to the C# compiler/runtime in general and would very much welcome a solution in that context. However, I would also appreciate a Bogus-specific solution, if there is any.

public class Foo
{
    public string Id { get; set; }
    public dynamic Attributes { get; set; }
}

Using the Bogus library I want to configure a Faker rule, like so:

public static IReadOnlyCollection<Foo> FakeFoos(int amount)
{
    var config = new Faker<Foo>()
        .StrictMode(false)
        .RuleFor(x => x.Id, y => y.Random.Guid().ToString("N"))
        .RuleFor(x => x.Attributes.......)  // <-- this here
    return config.Generate(amount);
}

Let's just say I want to set foo.Attributes.Percentage to an integer. I've tried several ways, all resulting in different errors on dotnetcore 3.1.

Example 1:

// Error [CS1963] An expression tree may not contain a dynamic operation
.RuleFor(x => x.Attributes.Percentage, y => y.Random.Int(70, 90))

Example 2:

// Error System.ArgumentException : Your expression '(x.Attributes As SomeWrappingClass).Percentage' cant be used. 
// Nested accessors like 'o => o.NestedO...
.RuleFor(x => (x.Attributes as SomeWrappingClass).Percentage, y => y.Random.Int(70, 90))

Example 3:

// Error RuntimeBinderException : Cannot perform runtime binding on a null reference
.RuleFor(x => x.Attributes, y => new SomeWrappingClass
{
    Percentage = y.Random.Int(70, 90),
});

Does anyone have an idea or suggestion how to set dynamic properties in this case?


Solution

  • First off, thank you for using Bogus!

    Using the following code:

    void Main()
    {
       var foo = FakeFoos(1).First();
       foo.Dump();
       Console.WriteLine($"Percentage: {foo.Attributes.Percentage}");
    }
    

    Option 1 - Anonymous Type

    The most elegant way to use dynamic would be to use an anonymous type:

    public static IReadOnlyCollection<Foo> FakeFoos(int amount)
    {
       var config = new Faker<Foo>()
           .StrictMode(false)
           .RuleFor(x => x.Id, y => y.Random.Guid().ToString("N"))
           .RuleFor(x => x.Attributes, y => new { Percentage = y.Random.Int(70, 90) });
       return config.Generate(amount);
    }
    

    enter image description here


    Option 2 - ExpandoObject

    More conventionally, using an ExpandoObject:

    public static IReadOnlyCollection<Foo> FakeFoos(int amount)
    {
       var config = new Faker<Foo>()
           .StrictMode(false)
           .RuleFor(x => x.Id, y => y.Random.Guid().ToString("N"))
           .RuleFor(x => x.Attributes, y =>
                {
                   dynamic eo = new ExpandoObject();
                   eo.Percentage = y.Random.Int(70, 90);
                   return eo;
                });
       return config.Generate(amount);
    }
    

    enter image description here


    Hope that helps, let me know if you have any more questions. :)