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

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

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

начиная с версии 3, MediatR теперь принимает второй необязательный параметр Send, a 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());