You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Moq中通用配置SetReturnsDefault返回DatabaseResult<T>实例?

Moq统一配置返回实例的两种方案

一、针对DatabaseResult的快速统一配置

如果只需要给IDataRepository中所有返回DatabaseResult<T>的方法统一设置返回实例,无需全局修改Moq默认行为,可以直接通过泛型匹配+反射实现:

var mockRepo = new Mock<IDataRepository>();

// 匹配所有返回DatabaseResult<T>的方法
mockRepo.Setup(m => m.It.IsAnyMethod(It.IsAny<object[]>()))
    .Returns((MethodInfo method) =>
    {
        var returnType = method.ReturnType;
        // 校验是否为DatabaseResult<>泛型类型
        if (!returnType.IsGenericType || returnType.GetGenericTypeDefinition() != typeof(DatabaseResult<>))
            return null;

        var innerType = returnType.GetGenericArguments()[0];
        // 创建DatabaseResult<T>实例(要求类有无参构造函数)
        var dbResult = Activator.CreateInstance(returnType);
        
        // 给DatabaseResult的Data属性赋值为对应T的新实例
        var dataProp = returnType.GetProperty(nameof(DatabaseResult<object>.Data));
        if (dataProp != null)
        {
            object innerInstance;
            // 集合接口返回List<T>实现,类/记录/值类型返回新实例
            if (innerType.IsInterface && innerType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
            {
                innerInstance = Activator.CreateInstance(typeof(List<>).MakeGenericType(innerType.GetGenericArguments()[0]));
            }
            else if (innerType.IsClass || innerType.IsRecord() || innerType.IsValueType)
            {
                innerInstance = Activator.CreateInstance(innerType);
            }
            else
            {
                innerInstance = null;
            }
            dataProp.SetValue(dbResult, innerInstance);
        }

        return dbResult;
    });

二、全局通用配置(覆盖所有Mock的默认行为)

如果希望所有Moq创建的Mock对象都自动处理:类/记录/结构返回新实例、集合接口返回List<T>等.NET实现,同时包含DatabaseResult<T>的逻辑,可以自定义DefaultValueProvider:

1. 自定义默认值提供器

public class CustomMockDefaultValueProvider : DefaultValueProvider
{
    protected override object GetDefaultValue(Type type, Mock mock)
    {
        // 优先处理DatabaseResult<T>
        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(DatabaseResult<>))
        {
            var innerType = type.GetGenericArguments()[0];
            var dbResult = Activator.CreateInstance(type);
            // 递归调用当前逻辑给Data属性赋值
            var dataValue = GetDefaultValue(innerType, mock);
            type.GetProperty(nameof(DatabaseResult<object>.Data))?.SetValue(dbResult, dataValue);
            return dbResult;
        }

        // 处理集合接口:IEnumerable<T>、ICollection<T>、IList<T>统一返回List<T>
        if (type.IsGenericType)
        {
            var genericDef = type.GetGenericTypeDefinition();
            if (genericDef == typeof(IEnumerable<>) || genericDef == typeof(ICollection<>) || genericDef == typeof(IList<>))
            {
                var listType = typeof(List<>).MakeGenericType(type.GetGenericArguments()[0]);
                return Activator.CreateInstance(listType);
            }
        }

        // 处理类、记录、值类型(string默认返回空字符串)
        if (type == typeof(string))
        {
            return string.Empty;
        }
        else if (type.IsClass || type.IsRecord() || type.IsValueType)
        {
            return Activator.CreateInstance(type);
        }

        // 其他类型沿用Moq默认逻辑
        return base.GetDefaultValue(type, mock);
    }
}

2. 使用自定义提供器

创建Mock时指定该提供器即可生效:

var mockRepo = new Mock<IDataRepository>() 
{ 
    DefaultValueProvider = new CustomMockDefaultValueProvider(),
    DefaultValue = DefaultValue.Mock // 配合自定义提供器启用逻辑
};

注意事项

  • 上述代码要求目标类型(DatabaseResult<T>、T、集合实现类)必须有无参数构造函数,如果是有参构造,需要修改Activator.CreateInstance的重载,传入构造参数或结合依赖注入容器创建。
  • 如果DatabaseResult<T>有需要初始化的元数据(比如Success属性默认设为true),可以在创建实例后额外赋值。

内容的提问来源于stack exchange,提问作者user1151923

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 12:23:18