Как смоделировать профиль отображения с параметрами отображения - PullRequest
0 голосов
/ 01 мая 2020

У меня есть профиль сопоставления, который использует 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

Сообщение:

Unsupported expression: opt => 
opt.Items.Add(Services.ClockService.Interfaces.IClockService.Name,
value(Hosts.Api.Tests.TransactionsControllerTests)._systemClock)

1 Ответ

1 голос
/ 01 мая 2020

Обычно я не издеваюсь над картографом; Я думаю, что большую часть времени интеграционное тестирование профилей сопоставления - это хорошая вещь, однако я делаю это время от времени по мере необходимости.

Начнем с определения соответствия для параметров:

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()));
...