Насмешливый MediatR 3 с Moq

Недавно мы начали использовать MediatR, чтобы позволить нам деактивировать действия контроллера, поскольку мы реорганизуем большой портал для клиентов и преобразуем его в C#. В рамках этого мы также расширяем охват модульных тестов, но у меня возникла проблема при попытке издеваться над самим MediatR.

Команда выполняет кучу вещей, чтобы инициировать процесс, и часть этого - отправка уведомления. Само уведомление обрабатывается его собственным обработчиком и, следовательно, должно проходить собственный модульный тест, поэтому я хочу издеваться над MediatR, чтобы this.mediator.Send(message) Звонок на самом деле ничего не делает. Обработчик возвращает объект, но мы не заботимся о нем в этом контексте, поэтому со всеми намерениями и целями мы рассматриваем его как void вернуть. Я просто хочу убедиться, что Send был вызван один раз как часть теста. Тем не менее Send метод бросает NullReferenceException и я не знаю почему.

Начиная с версии 3, MediatR теперь принимает второй необязательный параметр Send, CancellationTokenи деревья выражений требуют явного задания их, поэтому вы должны указать значение. Я не сталкивался с этим раньше, и я думаю, что это может быть частью проблемы, но это может быть путаницей с моей стороны.

Вот урезанная иллюстрация.

SUT

public class TransferHandler : IAsyncRequestHandler<TransferCommand, TransferResult>
{
    private readonly IMediator mediator;

    public TransferHandler(IMediator mediator)
    {
        this.mediator = mediator;
    }

    public async Task<TransferResult> Handle(TransferCommand message)
    {
        // Other stuff.
        var notification = new TransferNotificationCommand()
        {
            ClientId = message.clientId,
            OfficeId = message.OfficeId,
            AuthorityFileId = letter?.Id
        };

        await this.mediator.Send(notification);    // <=== This is where we get a NullReferenceException, even though nothing is actually null (that I can see).

        return new TransferResult()
        {
            Transfer = transfer,
            FileId = letter?.Id
        }
    }
}

Тестовое задание

public class TransferHandlerTests
{
    [Theory]
    [AutoData]
    public async void HandlerCreatesTransfer(Mock<IMediator> mockMediator)
    {
        // Note that default(CancellationToken) is the default value of the optional argument.
        mediator.Setup(m => m.Send(It.IsAny<TransferNotificationCommand>(), default(CancellationToken))).Verifiable("Notification was not sent.");

        var handler = new TransferHandler(mediator.Object);

        var actual = await handler.Handle(message);

        mediator.Verify(x => x.Send(It.IsAny<CreateIsaTransferNotificationCommand>(), default(CancellationToken)), Times.Once());
    }
}

Что мне не хватает? Я чувствую, что где-то допустил фундаментальную ошибку, но не знаю где.

1 ответ

Решение

Вам нужно обработать ожидание асинхронной операции Send методы, как они возвращают задачи.

/// <summary>
/// Asynchronously send a request to a single handler
/// </summary>
/// <typeparam name="TResponse">Response type</typeparam>
/// <param name="request">Request object</param>
/// <param name="cancellationToken">Optional cancellation token</param>
/// <returns>A task that represents the send operation. The task result contains the handler response</returns>
Task<TResponse> Send<TResponse>(IRequest<TResponse> request, CancellationToken cancellationToken = default(CancellationToken));

/// <summary>
/// Asynchronously send a request to a single handler without expecting a response
/// </summary>
/// <param name="request">Request object</param>
/// <param name="cancellationToken">Optional cancellation token</param>
/// <returns>A task that represents the send operation.</returns>
Task Send(IRequest request, CancellationToken cancellationToken = default(CancellationToken));

Это означает, что вам нужно, чтобы макет вернул задачу, чтобы асинхронный процесс мог продолжить процесс.

mediator
    .Setup(m => m.Send(It.IsAny<TransferNotificationCommand>(), It.IsAny<CancellationToken>()))
    .ReturnsAsync(new Notification()) //<-- return Task to allow await to continue
    .Verifiable("Notification was not sent.");

//...other code removed for brevity

mediator.Verify(x => x.Send(It.IsAny<CreateIsaTransferNotificationCommand>(), It.IsAny<CancellationToken>()), Times.Once());
Другие вопросы по тегам