如何将 Mediatr 实例化为单元测试的一部分?

Posted

技术标签:

【中文标题】如何将 Mediatr 实例化为单元测试的一部分?【英文标题】:How to instantiate Mediatr as part of a Unit Test? 【发布时间】:2019-08-21 21:58:53 【问题描述】:

我正在尝试为基于 CQRS/ES 模式的 MVC Core 2.2 应用程序构建一个 xUnit 测试项目。我在 MVC 应用程序中使用 MediatR 作为我的 CQRS/ES 模式的一部分。

在我想测试的命令之一中,我注入 MediatR 以在客户记录更新后发布事件。有点像这样:

public class UpdateCustomerCommandHandler : IRequestHandler<UpdateCustomerCommand>

    public IMediator Mediator  get; set;  

    public UpdateCustomerCommandHandler(IMediator mediator)
    
        Mediator = mediator;
    

    public Task<Unit> Handle(UpdateCustomerCommand request, CancellationToken cancellationToken)
    
        //do some stuff

        Mediator.Publish(new CustomersChanged());

        return Task.FromResult(new Unit());
    

在为此命令编写单元测试时,我显然还必须创建一个 MediatR 实例(或模型),然后在测试执行期间将其传递给命令。

[Fact]
public async void UpdateCustomerCommand_CustomerDataUpdatedOnDatabase()

    //Arange

    var mediator = new Mediator(); // doesn't work that way..

    UpdateCustomerCommand command = new UpdateCustomerCommand();
    UpdateCustomerCommandHandler handler = new UpdateCustomerCommandHandler(mediator);

    //Act
    Unit x = await handler.Handle(command, new System.Threading.CancellationToken());

    //Asert
    //Do the assertion

但是,实例化 MediatR(在 MVC 应用程序之外,我可以利用现有的依赖注入实现)似乎不是那么简单,坦率地说,我实际上不明白如何在我的测试方法中做。

我知道我可能会使用 MediatR 已经为其提供了实现(Ninject 等)的依赖注入框架,但实际上我不想在我的单元测试中使用除 MediatR 之外的任何其他第三方库,只是为了创建一个实例。

是否有一种更简单的方法来实例化我可能已经监督的 MediatR?

【问题讨论】:

【参考方案1】:

or a mockup 是正确的 - 你需要 mock IMediator

那里有一些模拟库:

起订量 FakeItEasy N替换

Moq 是最受欢迎的之一,因此,以您的测试为例:

[Fact]
public async void UpdateCustomerCommand_CustomerDataUpdatedOnDatabase()

    //Arange
    var mediator = new  Mock<IMediator>();

    UpdateCustomerCommand command = new UpdateCustomerCommand();
    UpdateCustomerCommandHandler handler = new UpdateCustomerCommandHandler(mediator.Object);

    //Act
    Unit x = await handler.Handle(command, new System.Threading.CancellationToken());

    //Asert
    //Do the assertion

    //something like:
    mediator.Verify(x=>x.Publish(It.IsAny<CustomersChanged>()));

【讨论】:

好的,我理解这个概念(同时也很惊讶)。但是,如果我真的想检查任何通知的接收者的状态(比如说,检查我想检查是否删除了某个缓存,基于 CustomersChanged 事件)?这基本上迫使我制作 MediatR 的真实实例 => 有没有简单的方法可以做到这一点? @Roland 那么你不再是unit testing 你是集成测试。您需要创建一个“真正的”中介,连接所有依赖项等......避免这种情况。单独测试部件。 你是对的。我走错了路!感谢您的回答;帮了我很多! @Alex 独立测试作为第一步,但至少有一些集成测试很好而且非常好。我们在一个项目中遇到过这个问题,其中所有的东西都被单元测试很好地覆盖了,但是几乎所有的集成测试都会在某些时候导致非常大的问题。很多单元测试,但肯定有一些集成。 如果目的是“测试一个命令”,这个解决方案不只是测试一个命令被调用。它测试命令处理程序是否调用了 Publish 方法。这只是样板代码。需要测试的真正重要的代码是特定 MediatR 命令或查询中的代码。这就是真正的工作完成的地方,也是一个命令或查询与另一个不同的地方。【参考方案2】:

为了扩展已接受的答案,如果您绝对需要使用 MediatR 测试两个服务之间的交互,您始终可以使用回调来最小化每个发布/处理程序对:

_mediator = new Mock<IMediator>();
_mediator.Setup(m => m.Publish(It.IsAny<YourNotification>(), It.IsAny<CancellationToken>()))
   .Callback<YourNotification, CancellationToken>((notification, cToken) => 
      _yourHandlerService.Handle(notification, cToken));

上面的代码,基本上是说如果_mediator通过Publish方法得到YourNotification,那么它会通过Handle方法转发给_yourHandlerService。您可以对您要处理的每种类型的中介 INotification 重复该 Setup

【讨论】:

以上是关于如何将 Mediatr 实例化为单元测试的一部分?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 MediatR 进行测试

MediatR 和 CQRS 测试。如何验证调用了该处理程序?

如何使用 FluentAssertions 在 XUnit 中测试 MediatR 处理程序

如何在 .net framework 4.7 上使用 Mediatr 进行集成测试?

如何使用FluentAssertions在XUnit中测试MediatR处理程序

集成测试从 .NET 5 迁移到 6 在 Mediatr 命令中失败