Насмешливый 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());