我有一个映射配置文件,使用 IDictionary<string, object>
来传递一个值。我是这样使用的。
var viewModel = _mapper.Map<TransactionDetailsViewModel>(
transaction,
opt =>
{
opt.Items.Add( typeof(IClockService).Name, _systemClock );
}
);
我试着在单元测试中模拟它,但总是得到... NotSupportedException
在这里。
Mock<IMapper> _mockMapper = new Mock<IMapper>();
_mockMapper
.Setup(
mm => mm.Map<TransactionDetailsViewModel>(
domainModel,
opt => opt.Items.Add( typeof(IClockService).Name, _systemClock )
)
)
.Returns( viewModel );
正确的模拟方法是什么?
下面是 StackTrace
:
at Moq.MatcherFactory.CreateMatcher(Expression expression)
at Moq.MatcherFactory.CreateMatcher(Expression argument, ParameterInfo parameter)
at Moq.MatcherFactory.CreateMatchers(IReadOnlyList`1 arguments, ParameterInfo[] parameters)
at Moq.InvocationShape..ctor(LambdaExpression expression, MethodInfo method, IReadOnlyList`1 arguments, Boolean exactGenericTypeArguments)
at Moq.ExpressionExtensions.<Split>g__Split|4_1(Expression e, Expression& r, InvocationShape& p)
at Moq.ExpressionExtensions.Split(LambdaExpression expression)
at Moq.Mock.Setup(Mock mock, LambdaExpression expression, Condition condition)
at Moq.Mock`1.Setup[TResult](Expression`1 expression)
at Hosts.Api.Tests.TransactionsControllerTests.GetTransaction_WhenAdvance_ConvertsSpread() in C:\Users\RS\source\repos\Project\src\UnitTests\Hosts.Api.Tests\Operations\Transactions\TransactionsControllerTests.cs:line 211
留言内容:我有一个使用IDictionary的映射配置文件。
Unsupported expression: opt =>
opt.Items.Add(Services.ClockService.Interfaces.IClockService.Name,
value(Hosts.Api.Tests.TransactionsControllerTests)._systemClock)
通常我不会模拟映射器,我认为大多数时候集成测试映射配置文件是一件好事,然而我时常会根据需要做。
首先为选项指定一个匹配器。
mapperMock.Setup(x => x.Map<TransactionDetailsViewModel>(
transaction,
It.Is<Action<IMappingOperationOptions>>(opt => AddsItem(opt, nameof(IClockService), systemClock)))).Returns(viewModel);
你不能对委托本身进行匹配,但在这种情况下,你可以对它所做的事情进行匹配--向项目字典中添加一个项目。我将实际的比对委托给了方法 AddsItem
来保持代码的可读性。
public bool AddsItem(Action<IMappingOperationOptions> providedOptions, string key, object value)
{
var mappingOptions = new CustomMappingOperationOptions();
providedOptions.Invoke(mappingOptions);
return mappingOptions.Items.Any(x => x.Key.Equals(key) && x.Value.Equals(value));
}
基本上比较解析了委托人在做什么,并根据它是否做了我们预期的事情而返回true或false。可以根据需要砍掉和改变比较,我在这里做了一些假设。CustomMappingOperationOptions
只是一个快速的本地实现 IMappingOperationOptions
因为具体的实现看起来有点麻烦,而我们在测试中不需要它。
最后,把它拉到一起。
var mapperMock = new Mock<IMapper>();
mapperMock.Setup(x => x.Map<TransactionDetailsViewModel>(
transaction,
It.Is<Action<IMappingOperationOptions>>(opt => AddsItem(opt, nameof(IClockService), new ClockService())))).Returns(viewModel);
var mockedMapper = mapperMock.Object;
var result = mockedMapper.Map<TransactionDetailsViewModel>(transaction, opt => opt.Items.Add(nameof(IClockService), new ClockService()));