【问题标题】:How can I dynamically set up a mock for an enum property?如何为枚举属性动态设置模拟?
【发布时间】:2020-03-17 20:28:07
【问题描述】:

我正在尝试基于 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;
}

【问题讨论】:

  • 你不能做一个property.GetType()然后使用激活器创建枚举然后赋值吗?
  • 创建枚举值不是问题。我在 convertToEnum 方法中这样做。问题是创建 propertyExpression。使用 Activator 你不会得到编译时类型。并且 moq 方法需要编译时类型。

标签: c# generics enums moq


【解决方案1】:

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

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

    //...
}

【讨论】:

  • 谢谢!这正是我想要的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-21
  • 2013-05-09
  • 1970-01-01
  • 2012-02-13
  • 2021-02-03
相关资源
最近更新 更多