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

How can I dynamically set up a mock for an enum property?

我正在尝试根据 xml 数据动态创建模拟。这对大多数类型都适用,但枚举有点棘手。这 ???在我的代码中需要枚举的类型。但显然类型在编译时是未知的,所以我不得不求助于反思。我可能必须使用 MakeGenericMethod 直接或间接调用 Expression.Lambda,但这似乎只是解决了问题,因为 mock.Setup 也需要编译时类型。那就是我卡住的地方。感谢任何帮助。

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;
}

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

//...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
    }

    //...
}