如何将 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 和 CQRS 测试。如何验证调用了该处理程序?
如何使用 FluentAssertions 在 XUnit 中测试 MediatR 处理程序
如何在 .net framework 4.7 上使用 Mediatr 进行集成测试?