11

我更喜欢让我的处理程序不受难以测试的 ASP.NET 基础设施的影响(是的,即使在 ASP.NET Core 中也是如此)。但有时它会发生并且你有一个像 UserManager 的依赖项(我想知道有一天为什么它不是一个接口),HttpContext 等等,并且单元测试变成了一个模拟地狱。

我尝试使用集成测试来处理它,方法是创建一个 TestServer 并为每个 api 调用初始化所有 ASP.NET 基础结构。它工作得很好,但如果我想测试我的处理程序的简单逻辑,有时似乎有点矫枉过正。虽然它解决了模拟 ASP.NET 基础结构的技术问题,但它保留了将 ASP.NET 基础结构放入处理程序的架构问题(如果您这么认为的话)。

我想知道处理它的推荐方法是什么?

4

3 回答 3

7

我感觉到你的痛苦。我偶然发现了Jimmy Bogard的一篇精彩的博客文章,该文章使用 Martin Fowler 所说的皮下测试来解决这个问题。我将把深入的解释留给那些专家,但简而言之,皮下测试只是避免了 UI 中所有难以测试的方面。

无耻的插件:我目前正在编写一个 wiki,在 github 上的示例端到端项目中演示这些模式。这并不难理解,但对于 SO 答案来说,发布的代码可能太多了。

总结:

  • 如果您正确使用 MediatR,您的控制器应该非常薄,这使得测试它们毫无意义
  • 您要测试的是您的处理程序。
  • 但是,您希望将处理程序作为现实世界管道的一部分进行测试。

解决:

  1. 将 http 请求包装在事务中。
  2. 构建一个模拟应用程序 Startup.cs 的测试夹具
  3. 设置一个测试数据库服务器来执行查询和命令,但在每次测试后都会重置。

基本上就是这样。每次针对您的处理程序之一运行集成测试时:

  • 托管环境是模拟的,但您的应用程序是在真实世界的测试中启动的。
  • 您的查询或命令包含在模仿 DbContext 的事务中。
  • 处理程序针对真实数据库执行,然后重置。

我会在我的答案中添加更多代码示例,但在博客文章和我提供的 wiki 之间,遵循那里的代码示例要容易得多。

编辑 8/2021:

坚持源头。Jimmy Bogard 在他的github 页面上保持 contoso 大学项目的最新状态。另一个伟大的、更高级的例子是 Kamil Grzybek 的模块化单体项目。这也在他的github 页面上定期更新。

于 2017-02-07T15:34:10.343 回答
1

我想说这取决于你最终想要获得的信心水平。如果您想确保整个系统按预期工作,那么使用 aTestServer的集成测试可能是要走的路。

不过,MediatR 的一个优点是它允许您将业务逻辑与使用它的应用程序解耦,这就是为什么在最顶层,比如在控制器中,没有逻辑,只有对中介的委托。

话虽如此,有时您的逻辑需要来自托管应用程序的信息是对的。一个示例是发出请求的用户,该请求可在 HTTP 上下文中访问。

在这种情况下,如果您想避免必须设置一个测试 HTTP 服务器来测试您的逻辑是否有效,您可以在抽象中表示该信息,然后您的处理程序将依赖该抽象。然后,您的测试可以模拟该依赖关系,同时将真实系统用于其他所有内容。

那有意义吗?

于 2017-02-02T00:06:24.023 回答
1

无论是否使用 Mediatr,您都应该始终尝试仅在控制器中将其传递给非常基本的逻辑,并从那里调用注入的业务逻辑类来完成实际工作。当您向此业务逻辑注入接口时,您的控制器的依赖关系很容易在单元测试中模拟,您的测试可以专注于它们是否正确实现了这些接口并且只执行路由输入/输出的基本工作。并且您的实际业务逻辑可以更容易地进行测试。

对于那些静态的类,例如读取 web.config 设置,我非常喜欢的一种策略是围绕它们创建一个接口包装类。虽然 ConfigurationManager 是静态的,但我仍然可以编写一个带有接口的常规类,在该接口上放置方法或属性以从 Configuration Manager 读取特定设置(最好是语义命名的)。现在我可以轻松地在我的测试中模拟任何配置的设置(或没有它),只需模拟接口并设置不同的返回值。

于 2017-02-02T19:19:29.923 回答