【问题标题】:How to instantiate Mediatr as part of a Unit Test?如何将 Mediatr 实例化为单元测试的一部分?
【发布时间】:2023-03-24 23:58:01
【问题描述】:

我正在尝试为基于 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?

【问题讨论】:

    标签: cqrs 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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-09
      相关资源
      最近更新 更多