使用 Moq 模拟 MediatR 3 [英] Mocking MediatR 3 with Moq

查看:44
本文介绍了使用 Moq 模拟 MediatR 3的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们最近开始使用 MediatR 来消除控制器操作的混乱,因为我们重构了一个面向客户的大型门户并将其全部转换为 C#.作为其中的一部分,我们也在增加我们的单元测试覆盖率,但我在尝试模拟 MediatR 本身时遇到了问题.

We've recently started using MediatR to allow us to de-clutter controller actions as we re-factor a large customer facing portal and convert it all to C#. As part of this we are increasing our unit test coverage as well, but I've hit a problem when trying to mock MediatR itself.

该命令执行一系列操作来启动进程,其中一部分是发送通知.通知本身由它自己的处理程序处理,因此会受到它自己的单元测试的影响,所以我想模拟 MediatR 以便 this.mediator.Send(message) 调用并没有真正做到任何事物.处理程序确实返回一个对象,但在此上下文中我们不关心它,因此出于所有意图和目的,我们将其视为 void 返回.我只是想验证 Send 是否作为测试的一部分被调用过一次.但是,Send 方法抛出了一个 NullReferenceException,我不知道为什么.

The command does a bunch of stuff to initiate a process and part of this is sending a notification. The notification itself is dealt with by its own handler and therefore would be subject to its own unit test so I want to mock MediatR so that the this.mediator.Send(message) call doesn't really do anything. The handler does return an object but we don't care about it in this context so to all intents and purposes we are treating it as a void return. I just want to verify that Send has been called once as part of the test. However, the Send method is throwing a NullReferenceException and I don't know why.

从版本 3 开始,MediatR 现在在 Send 上采用第二个可选参数,一个 CancellationToken,并且表达式树要求您显式设置它们,因此您必须指定一个值.我以前没有遇到过这种情况,在我看来,这可能是问题的一部分,但这可能是我的困惑.

As of version 3, MediatR now takes a second optional parameter on Send, a CancellationToken, and expression trees require you to explicitly set them so you must specify a value. I've not encountered this before and in my mind I feel that this might be part of the problem but this may be conflation on my part.

这是一个简化的插图.

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.
        mockMediator.Setup(m => m.Send(It.IsAny<TransferNotificationCommand>(), default(CancellationToken))).Verifiable("Notification was not sent.");

        var handler = new TransferHandler(mockMediator.Object);

        var actual = await handler.Handle(message);

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

我错过了什么?我觉得我在某个地方犯了一个根本性的错误,但我不确定在哪里.

What am I missing? I feel like I've made a fundamental mistake somewhere but I'm not sure where.

推荐答案

您需要在 Send 方法返回任务时处理异步操作的 await.

You need to handle the await of the async operation of the Send methods as they return 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));

这意味着您需要让模拟返回一个任务以允许异步进程继续流程

That means you need to have the mock return a task to allow the async process to continue the flow

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

这篇关于使用 Moq 模拟 MediatR 3的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆