如何在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
相关产品推荐
相关产品推荐

