我们的单元测试中有很多重复的设置,因为我们使用的是 MediatR 库。我们经常看到这样的行:
mockMediator.Setup(m => m.Send(It.IsAny<Command>(), It.IsAny<CancellationToken>())
.ReturnsAsync("ok");
我想制作一个扩展方法,简化语法以更加强调Command
,并且如果我们愿意,仍然可以让我们有条件,例如:
mockMediator.SetupSend(It.IsAny<Command>())
.ReturnsAsync("ok");
// or
mockMediator.SetupSend(It.Is<Command>(c => c.IsSomething))
.ReturnsAsync("ok");
此代码可以构建,但不起作用(模拟始终返回 null):
public static ISetup<IMediator, Task<TResult>> SetupRequest<TResult>(this Mock<IMediator> mockMediator, IRequest<TResult> request) {
return mockMediator.Setup(m => m.Send(request, It.IsAny<CancellationToken>()));
}
从根本上来说:如何将参数传递到稍后进入表达式主体的方法中?是否有受支持的方法来为常用设置制作像这样的 Moq“助手”功能?
最佳答案
如果您只是通过委托(delegate)推迟参数的评估,您现有的扩展方法将起作用:
public static ISetup<IMediator, Task<TResult>> SetupRequest<TResult>(this Mock<IMediator> mockMediator, Func<IRequest<TResult>> request) {
return mockMediator.Setup(m => m.Send(request.Invoke(), It.IsAny<CancellationToken>()));
}
用作
mockMediator.SetupSend(() => It.IsAny<Command>())
.ReturnsAsync("ok");
// or
mockMediator.SetupSend(() => It.Is<Command>(c => c.IsSomething))
.ReturnsAsync("ok");
从根本上来说,它的行为应该与 Jason 在最常见用例中建议的答案相同。不过,这并没有真正记录 Moq 的行为,因此将 It.Is
与表达式树结合使用会更安全。
好的,但是为什么呢?
推迟此处参数的求值可确保在求值 It.IsAny
方法调用之前创建“匹配器上下文”。这可确保 Moq 在遇到 IsAny
或 Is
表达式时能够注意到正在计算哪个参数。
It.Is
表达式自然地强制执行此操作,因为它采用表达式树,而永远不会急切地求值。
另一种选择是手动构建表达式树,但是围绕它构建一个漂亮的 API 表面区域可能仍然需要对参数进行某种延迟评估,因此它可能不会比现有的获得太多 yield 答案。
关于c# - 尝试使用辅助方法简化起订量设置语法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63475079/