Mocking MediatR 3 with Moq

We recently started using MediatR to allow us to eliminate controller actions as we rearrange the large client portal and convert everything to C #. As part of this, we are also increasing the coverage of unit test, but I ran into a problem while trying to mock MediatR itself.

The team does a ton of things to initiate the process, and part of it sends a notification. The notification itself is processed by its own handler and therefore will obey its own unit test, so I want to make fun of MediatR so that calling this.mediator.Send(message) really does nothing. The handler returns the object, but we do not care about this in this context, therefore, for all purposes and purposes, we consider it as a void return. I just want to verify that Send was called once as part of the test. However, the Send method throws a NullReferenceException , and I don't know why.

Starting with version 3, MediatR now accepts the second optional parameter on Send , a CancellationToken , and expression trees require that you explicitly set them so that you specify a value. I have not encountered this before and, in my opinion, I feel that this may be part of the problem, but it may be a merger on my part.

Here is a snapshot.

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 } } } 

Test

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

What am I missing? I feel like I made a fundamental mistake, but I don’t know where.

+5
source share
1 answer

You need to handle the wait for the asynchronous operation of the Send methods when returning tasks.

 /// <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)); 

This means that you need the mock to return the task, so that the asynchronous process continues the thread

 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()); 
+8
source

Source: https://habr.com/ru/post/1266402/


All Articles