如何动态为枚举属性设置模拟?

时间:2019-11-21 19:58:40

标签: c# generics enums moq

我正在尝试基于xml数据动态创建一个模拟。这对于大多数类型都可以正常工作,但枚举却有些棘手。 ???在我的代码中期望枚举的类型。但是显然类型在编译时是未知的,因此我不得不求助于反射。我可能必须使用MakeGenericMethod来直接或间接调用Expression.Lambda,但这似乎只是将问题转移为模拟。安装程序也需要编译时类型。那就是我被困住的地方。任何帮助表示赞赏。

public static Mock<T> DeserializeMock<T>(XElement node)
    where T : class
{
    var mock = new Mock<T>();

    foreach (var property in PropertyInfoHelper.EnumeratePublicAndInternalProperties(typeof(T)))
    {
        var attribute = node.Attribute(property.Name);

        if (property.PropertyType == typeof(string))
        {
            var parameter = Expression.Parameter(typeof(T));
            var body = Expression.PropertyOrField(parameter, property.Name);
            var propertyExpression = Expression.Lambda<Func<T, string>>(body, parameter);
            mock.Setup(propertyExpression).Returns(attribute.Value);
        }

// ... other types omitted for brevity...

        else if (property.PropertyType.IsEnum)
        {
            var parameter = Expression.Parameter(typeof(T));
            var body = Expression.PropertyOrField(parameter, property.Name);
            var propertyExpression = Expression.Lambda<Func<T, ???>>(body, parameter);
            mock.Setup(propertyExpression).Returns(convertToEnum(attribute.Value, property.PropertyType));
        }
    }

    return mock;
}

1 个答案:

答案 0 :(得分:0)

您最终将不得不使用反射来获取设置的类型

//...omitted for brevity...

else if (property.PropertyType.IsEnum) {
    var parameter = Expression.Parameter(typeof(T));
    var body = Expression.PropertyOrField(parameter, property.Name);
    var delegateType = typeof(Func<,>).MakeGenericType(typeof(T), property.PropertyType);
    var propertyExpression = Expression.Lambda(delegateType, body, parameter);
    var value = convertToEnum(attribute.Value, property.PropertyType);

    var setup = mock.GetType().GetMethods()
        .FirstOrDefault(m => m.Name == "Setup" && m.ContainsGenericParameters)
        .MakeGenericMethod(property.PropertyType);
    var result = setup.Invoke(mock, new object[] { propertyExpression });
    var returns = result.GetType().GetMethod("Returns", new[] { property.PropertyType });
    returns?.Invoke(result, new object[] { value });

}

//...omitted for brevity...

我还根据原始代码假设,在模拟中设置的所有成员都是虚拟成员或抽象成员。

概念证明

[TestClass]
public class MyTestClass {
    [TestMethod]
    public void MyTestMethod() {
        //Arrange
        XElement element = new XElement("root",
            new XAttribute("MyProperty1", "Hello World"),
            new XAttribute("MyEnumProperty", "Value2")
        );

        //Act
        var mock = DeserializeMock<MyClass>(element);

        //Assert
        var actual = mock.Object;
        actual.MyEnumProperty.Should().Be(MyEnum.Value2);
        actual.MyProperty1.Should().Be("Hello World");
    }

    public class MyClass {
        public virtual string MyProperty1 { get; set; }
        public virtual MyEnum MyEnumProperty { get; set; }
    }

    public enum MyEnum {
        Value1,
        Value2
    }

    //...
}