c# - 如何使用 moq 在 C# 中编写将接口(interface)属性映射到键值对的通用模拟

标签 c# .net generics lambda moq

我想编写一个为任何接口(interface)创建模拟的方法。

public T GetMock<T>(IDictionary<string, object> data) where T : class

我首先只关心属性 getter 。所有 getter 都应该返回存储在字典中的值。属性名称是该字典中的一个键。以下代码说明了预期用途:

    public interface IFoo
    {
        string Property1 { get; }
        int Property2 { get; }
        DateTime Property3 { get; }
    }


    [Test]
    public void TestY()
    {
        var data = new Dictionary<string, object>
        {
            {"Property1", "Hello"},
            {"Property2", 5},
            {"Property3", DateTime.Today}
        };

        var mock = GetMock<IFoo>(data);

        Assert.AreEqual("Hello", mock.Property1);
        Assert.AreEqual(5, mock.Property2);
        Assert.AreEqual(DateTime.Today, mock.Property3);
    }

关键是我想模拟任何接口(interface)。所以我的通用模拟创作看起来像:

    public T GetMock<T>(IDictionary<string, object> data) where T : class
    {
        var mock = new Mock<T>();
        var type = typeof(T);
        var properties = type.GetProperties();

        foreach (var property in properties)
        {
            var attributeName = property.Name;
            var parameter = Expression.Parameter(type);
            var body = Expression.Property(parameter, attributeName);
            var lambdaExpression = Expression.Lambda<Func<T, object>>(body, parameter);
            Func<object> getter = () => data[attributeName];
            mock.Setup(lambdaExpression).Returns(getter);
        }
        return mock.Object;
    }

它应该可以工作,但存在类型转换问题。测试失败并显示一条消息:

System.ArgumentException : Expression of type 'System.Int32' cannot be used for return type 'System.Object'

我想我缺少一些转换 lambda。有什么解决问题的建议吗?

最佳答案

我猜唯一的选择是使用反射,因为当前版本是 4.2,但仍然没有“Mock.Setup(Expression expr)”实现,正如 Patrick 所说。 所以,这是我的示例:

    public static class ConfigFactory<T> where T : class {
    static T cachedImplInstance;

    public static T BuildConfigGroupWithReflection() {
        if (cachedImplInstance == null) {
            Type interfaceType = typeof(T);
            MethodInfo setupGetMethodInfo = typeof(Mock<T>).GetMethod("SetupGet");
            Mock<T> interfaceMock = new Mock<T>();

            IDictionary<Type, MethodInfo> genericSetupGetMethodInfos = new Dictionary<Type, MethodInfo>();
            IDictionary<Type, MethodInfo> specificReturnsMethodInfos = new Dictionary<Type, MethodInfo>();

            if (setupGetMethodInfo != null)
                foreach (PropertyInfo interfaceProperty in interfaceType.GetProperties()) {
                    string propertyName = interfaceProperty.Name;
                    Type propertyType = interfaceProperty.PropertyType;

                    ParameterExpression parameter = Expression.Parameter(interfaceType);
                    MemberExpression body = Expression.Property(parameter, propertyName);
                    var lambdaExpression = Expression.Lambda(body, parameter);

                    MethodInfo specificSetupGetMethodInfo =
                        genericSetupGetMethodInfos.ContainsKey(propertyType) ?
                        genericSetupGetMethodInfos[propertyType] :
                        genericSetupGetMethodInfos[propertyType] = setupGetMethodInfo.MakeGenericMethod(propertyType);

                    object setupResult = specificSetupGetMethodInfo.Invoke(interfaceMock, new[] { lambdaExpression });
                    MethodInfo returnsMethodInfo = 
                        specificReturnsMethodInfos.ContainsKey(propertyType) ?
                        specificReturnsMethodInfos[propertyType] :
                        specificReturnsMethodInfos[propertyType] = setupResult.GetType().GetMethod("Returns", new[] { propertyType });

                    if (returnsMethodInfo != null)
                        returnsMethodInfo.Invoke(setupResult, new[] { Settings.Default[propertyName] });
                }                
            cachedImplInstance = interfaceMock.Object;
        }
        return cachedImplInstance;
    }
}

通知行“returnsMethodInfo.Invoke(setupResult, new[] { Settings.Default[propertyName] });” - 你可以把你的字典放在这里。

比如说,我们有接口(interface):

public interface IConfig {
    string StrVal { get; }
    int IntVal { get; }

    StringCollection StrsVal { get; }

    string DbConnectionStr { get; }

    string WebSvcUrl { get; }
}

然后,用法如下(假设我们的项目有相应的名称/类型/值的“设置”):

IConfig cfg0 = ConfigFactory<IConfig>.BuildConfigGroupWithReflection();

关于c# - 如何使用 moq 在 C# 中编写将接口(interface)属性映射到键值对的通用模拟,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23785808/

相关文章:

c# - 检查当前和外部 .Net 进程是否启用了性能计数器?

delphi - Delphi 泛型类可以从其类参数派生吗?

java - 如何重写以下方法而不出现未经检查的警告?

Swift:将类型从属性传递给泛型函数

c# - 如何在展开表单时自动移动和调整按钮和标签的大小?

c# - JSON 解析错误 : Missing a name for object member

c# - 从 C# 代码中删除 dataGridView1_CellContentClick() 会导致构建失败 - 为什么?

c# - OrderByChild 不会订购,Firebase 实时数据库

c# - 使用 Linq 从表中提取数据

C# utf8_解码