0

Here is info about our technical development environment :

  • .NET Core 3.1

  • PostgreSQL 14.2, compiled by Visual C++ build 1914, 64-bit

  • EntityFramework.Functions Version=1.5.0

  • Microsoft.EntityFrameworkCore.Design Version=5.0.17

  • Microsoft.EntityFrameworkCore.Tools Version=5.0.17

  • Npgsql.EntityFrameworkCore.PostgreSQL Version=5.0.10

In my application, I have a Generic Target Class, and it's corresponding Wrapper class and Wrapper interface.

public class TargetWrapper<T> : ITargetWrapper<T>
{
    private Target<T> _target;
   
    public TargetWrapper()
    {
        _target = new Target<T>();
    }

    protected internal TargetWrapper(Target<T> target)
    {
        _target = target;
    }

    public T Data 
    { 
        get { return _target.Data; } 
        set { _target.Data = value; } 
    }
}

I have the a Business Logic class called JohnDoeClass has the following code that uses the TargetWrapper which sets the generic type to a dynamic:

public class JohnDoeClass 
{
    public EmailRecord MaryDoeMethod(ITargetWrapper<dynamic> targetWrapper)
    {
        var dataset = targetWrapper.Data.someDynamicMetaDataEntity;

        return new BlahEntity()
        {
            Id = dataset ["id"],
            FaxId = dataset["fax_id"],
            FaxNumber = dataset["fax_number"],
            FaxLocation  = dataset["fax_location"],
            Subject = dataset["subject"]
        };
    } 
}

However, when it comes to Unit Mock Testing using the Moq Framework, I do Not know how to code the Moq Setup and it's Return in the following code:

public class JohnDoeClassTests
{    
    [Fact]
    public void MaryDoeMethodTest_Valid()
    {
        Mock<ITargetWrapper<dynamic>> targetWrapperMock = new Mock<ITargetWrapper<dynamic>>();
    
        dynamic returnedInfo = new ExpandoObject();
        returnedInfo.Id  = 3;
        returnedInfo.fax_id = 7;
        returnedInfo.fax_number = “416-949-9393”;
        returnedInfo.fax_location =”348 Rover Drive, New York, NY, USA”;
        returnedInfo.Subject = “Tax Rebate info 2023”;        

        graphQLResponseWrapperMock.Setup(r => r.Data …What toplace as code here in order Setup someDynamicMetaDataEntity dynamic property to return > something ?                                                                                  
            .Returns(What do I place here in order to return the returnedInfo dynamic object );
                                          
    } 
 }

How can I write code for Moq's Setup and its return in regards to mocking the dynamic property in question?

halfer
  • 19,824
  • 17
  • 99
  • 186
crazyTech
  • 1,379
  • 3
  • 32
  • 67

2 Answers2

2

First of all your MaryDoeMethod will not work. It will throw RuntimeBinderException:

Cannot apply indexing with to an expression of type 'System.Dynamic.ExpandoObject'

In order to fix the indexing problem you need to explicitly state that the ExpandoObject should be treated as IDictionary<string, object>

IDictionary<string, object> dataset = targetWrapper.Data.someDynamicMetaDataEntity;

Now, back to your test. I would suggest to use anonymous type to construct the expected return object and then use some helper method to convert that to dynamic.

The helper method

public ExpandoObject ToExpandoObject(object obj)
{
    IDictionary<string, object> expando = new ExpandoObject();

    foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(obj.GetType()))
    {
        expando.Add(property.Name, property.GetValue(obj));
    }

    return (ExpandoObject)expando;
}

This will not work for nested data structures. Either you need to extend it to work with them or create the outer ExpandoObject by hand. For the sake of simplicity let me show you the later one:

var expected = new
{
    someDynamicMetaDataEntity = new
    {
        id = 3,
        fax_id = 7,
        fax_number = "416 - 949 - 9393",
        fax_location = "348 Rover Drive, New York, NY, USA",
        subject = "Tax Rebate info 2023",
    }
};

IDictionary<string, object> expando = new ExpandoObject();
expando.Add(nameof(expected.someDynamicMetaDataEntity), ToExpandoObject(expected.someDynamicMetaDataEntity));

With that in our hand the mock setup is as simple as this

targetWrapperMock.Setup(w => w.Data).Returns(expando);

Here you can find a working example on dotnet fiddle: https://dotnetfiddle.net/vWC5VA

Peter Csala
  • 17,736
  • 16
  • 35
  • 75
1

Most of the the time dynamic is underpinned by a value, and just allows dynamic access. Given you're accessing your data as a key/value structure, you can mock a dictionary

Mock<ITargetWrapper<dynamic>> targetWrapperMock = new Mock<ITargetWrapper<dynamic>>();

Dictionary<string, object> data = new Dictionary<string, object>();
Dictionary<string, object> metadata = new Dictionary<string, object>
{
    ["Id"] = 3;
    ...
}

data.Add("someDynamicMetaDataEntity", metadata);

targetWrapperMock
    .Setup(r => r.Data)
    .Returns(data);
itsdaniel0
  • 1,059
  • 3
  • 11
  • 28