MediatR 处理程序中的 ASP.NET 基础结构
Posted
技术标签:
【中文标题】MediatR 处理程序中的 ASP.NET 基础结构【英文标题】:ASP.NET infrastructure in MediatR handlers 【发布时间】:2017-06-15 09:05:52 【问题描述】:我更喜欢让我的处理程序不受难以测试的 ASP.NET 基础设施的影响(是的,即使在 ASP.NET Core 中也是如此)。但有时它会发生,并且您有一个像 UserManager(我想知道有一天为什么它不是接口)、HttpContext 等的依赖项,并且单元测试变成了一个模拟地狱。
我尝试使用集成测试来处理它,方法是创建一个 TestServer 并为每个 api 调用初始化所有 ASP.NET 基础结构。它工作得很好,但如果我想测试我的处理程序的简单逻辑,有时似乎有点矫枉过正。虽然它解决了模拟 ASP.NET 基础结构的技术问题,但它保留了将 ASP.NET 基础结构放入处理程序的架构问题(如果您这么认为的话)。
我想知道有哪些推荐的处理方法?
【问题讨论】:
如果您对抽象 UserManager 和 Identity 感兴趣,请查看我在这里给出的答案:***.com/a/38464229/5233410 我发现this 和this 在保持处理程序清洁和易于测试方面非常有帮助。帖子分别来自 Jimmy Bogard 和 Patrick Lioi。 @trevorc,这些都是很好的方法。你能写出并回答你的经历吗? 【参考方案1】:我感觉到你的痛苦。我偶然发现了来自 Jimmy Bogard 的出色的 blog post,它使用 Martin Fowler 所称的 Subcutaneous Tests 来处理这个问题。我将把深入的解释留给那些专家,但简而言之,皮下测试只是避免了 UI 中所有难以测试的方面。
无耻插件:我目前正在编写一个 wiki,在 github 上的示例端到端项目中演示这些模式。这并不难理解,但对于 SO 答案来说,发布的代码可能太多了。
总结:
如果您正确使用 MediatR,您的控制器应该非常薄,这使得测试它们毫无意义。 您要测试的是您的处理程序。 但是,您希望将处理程序作为实际管道的一部分进行测试。解决:
-
将 http 请求包装在事务中。
构建一个模拟应用 Startup.cs 的测试夹具
设置测试数据库服务器以执行查询和命令,但在每次测试后都会重置。
基本上就是这样。每次针对您的处理程序之一运行集成测试时:
托管环境是模拟的,但您的应用程序是在实际测试中启动的。 您的查询或命令包含在一个模仿 DbContext 的事务中。 处理程序针对真实数据库执行,然后重置。我会在我的答案中添加更多代码示例,但在博客文章和我提供的 wiki 之间,更容易遵循那里的代码示例。
编辑 8/2021:
坚持源头。 Jimmy Bogard 在他的 github page 上保持 contoso 大学项目的最新状态。另一个伟大的、更高级的例子是 Kamil Grzybek 的模块化单体项目。他的github page 也会定期更新。
【讨论】:
【参考方案2】:我想说这取决于您最终想要获得的信心水平。如果您想确保整个系统按预期工作,那么使用 TestServer
的集成测试可能是可行的方法。
不过,MediatR 的一个优点是,它允许您将业务逻辑与使用它的应用程序解耦,这就是为什么在最顶层,比如在控制器中,没有逻辑,只有对中介的委托。
话虽如此,您是对的,有时您的逻辑需要来自托管应用程序的信息。一个示例是用户发出请求,该请求可在 HTTP 上下文中访问。
在这种情况下,如果您不想设置测试 HTTP 服务器来测试您的逻辑是否有效,您可以在抽象中表示该信息,然后您的处理程序将依赖该抽象。然后,您的测试可以模拟该依赖项,同时将真实系统用于其他一切。
这有意义吗?
【讨论】:
是的,确实如此。谢谢!我还考虑过使用 Mediatr 预处理来“丰富”具有特定于框架的东西的命令/查询(即,如果我需要获取某种 url,我不必注入 IUrlHelper),但这种方法似乎很麻烦。 我不确定我是否理解。你能详细说明一下吗?【参考方案3】:Mediatr 与否,您应该始终尝试在控制器中只传递非常基本的逻辑,并从那里调用注入的业务逻辑类来完成实际工作。当您向此业务逻辑注入接口时,您的控制器的依赖关系很容易在您的单元测试中模拟,您的测试可以专注于它们是否正确实现了这些接口并且只执行路由输入/输出的基本工作。并且您的实际业务逻辑可以更容易地进行测试。
对于那些静态类,例如读取 web.config 设置,我非常喜欢的一种策略是围绕它们创建一个接口包装类。虽然 ConfigurationManager 是静态的,但我仍然可以编写一个带有接口的常规类,在该接口上放置方法或属性以从 Configuration Manager 读取特定设置(最好是语义命名的)。现在我可以轻松地在我的测试中模拟任何配置的设置(或没有它),只需模拟接口并设置不同的返回值。
【讨论】:
以上是关于MediatR 处理程序中的 ASP.NET 基础结构的主要内容,如果未能解决你的问题,请参考以下文章
如何为 ASP.NET Core 注册和使用 MediatR 管道处理程序?
ASP.NET Core MediatR 错误:向容器注册处理程序
在 asp .net 核心中为 MediatR 库的发送和发布方法添加通用处理程序