C#单元测试中如何Mock foreach循环内调用的IMapper接口?
C# 单元测试中循环调用IMapper的Mock正确配置方法
问题场景
业务代码中通过IMapper逐行循环映射实体,核心代码如下:
foreach (var req in listRequestMasters) { var customReq = _mapper.Map<GridModel>(req); }
编写单元测试配置Mock时遇到两个问题:
- 第一种配置下断言可执行通过,但返回结果不符合预期,同一条记录重复出现多次:
_mockMapper.Setup(x => x.Map<GridModel>(It.IsAny<RequestMaster>())).Returns(requestGridModelMockData.FirstOrDefault()); - 第二种配置直接抛出
Moq.MockException异常,严格Mock模式下提示找不到对应调用的Setup配置:Moq.MockException : IMapperBase.Map(RequestMaster)
invocation failed with mock behavior Strict. All invocations on the
mock must have a corresponding setup.
对应错误配置代码:_mockMapper.Setup(x => x.Map<List<GridModel>>(It.IsAny<RequestMaster>())).Returns(requestGridModelMockData);
测试预期为接口返回结果与Mock数据源requestGridModelMockData完全一致,现有断言逻辑:
var result = await Service.GetHistoryRequestDetails(historyVMMockData); Assert.Equal(JsonConvert.SerializeObject(requestGridModelMockData), JsonConvert.SerializeObject(result));
错误原因
- 第一种配置错误:
Returns传入了固定的单个实体实例,Moq每次匹配到Map调用都会返回这同一个实例,循环执行多少次就返回多少次同一个对象,必然出现单条数据重复的问题。 - 第二种配置错误:业务代码实际调用的是泛型参数为
GridModel的单对象映射方法,但Setup配置的是泛型参数为List<GridModel>的集合映射方法,方法签名完全不匹配,严格Mock模式下找不到对应配置就会抛出异常。
正确配置方案
方案1:按调用顺序依次返回Mock数据
使用Moq的ReturnsInOrder方法,按传入顺序逐次返回Mock数据集中的元素,和循环逐行映射的逻辑完全匹配:
// 需确保已引入Moq命名空间 _mockMapper.Setup(x => x.Map<GridModel>(It.IsAny<RequestMaster>())) .ReturnsInOrder(requestGridModelMockData.ToArray());
注意:传入的数组长度需要和循环执行的次数保持一致,超出调用次数后会返回类型默认值。
方案2:根据入参动态匹配返回结果
如果Mock数据集和传入的源实体是一一对应的,可以在Returns中传入委托,根据传入的源对象动态匹配对应的返回值,灵活性更高,不受调用顺序、次数限制:
_mockMapper.Setup(x => x.Map<GridModel>(It.IsAny<RequestMaster>())) .Returns((RequestMaster source) => { // 按实际业务的唯一标识匹配对应Mock数据即可,示例以RequestId匹配为例 return requestGridModelMockData.First(item => item.RequestId == source.Id); });
该写法完全兼容严格Mock模式,逻辑更贴近AutoMapper的真实映射行为。
内容的提问来源于stack exchange,提问作者abbas ahmed
相关产品推荐
相关产品推荐

