如何使 DbContext in using 语句与单元测试一起工作?

How do I make DbContext in using statement work with unit tests?

我的情况是这样的:

  1. 我正在开发一个 ASP.NET MVC 项目,它没有实现存储库模式
  2. 所有 DbContext 调用都是从控制器层进行的
  3. DbContext 包装在 using 语句中,并根据需要实例化以用于内存管理而非连接池目的
  4. 我们需要对这些控制器进行单元测试,这意味着将 DbContext 注入控制器。
  5. 我们没有使用 IoC 容器。我将不得不在无参数构造函数中创建一个新实例,并在调用控制器操作的整个生命周期中使用它。
  6. 我无法改变这些条件中的大部分。

我可以改变的条件:

  1. 去掉 using 语句,如果有可靠的选项可以代替它的话。
  2. 承认此代码无法按原样进行测试,然后继续在其他地方花费精力。

所以,我的问题是:我如何将 DbContext 传递给构造函数,以便我可以模拟返回的响应,但保持 using 语句的可靠性,因为它们往往相互矛盾其他。

如果我需要停止使用 using 语句,我没问题,只要有一种方法可以确保每次执行完所请求的操作时都释放上下文。

对此有什么想法吗?

有这些限制,虽然困难,但并非不可能。您没有提到 Core 会让这变得轻而易举。

最有可能的是,创建一个接受 DbContext 对象的构造函数。在您的单元测试中,创建您的 "mocked" DbContext 并将其传递给控制器​​。如果控制器收到一个 DbContext,它不会创建它。

此外,考虑在进行任何测试之前创建一个用于测试目的的内存中 EF DbContext,并在测试完成后将其销毁。

在没有IOC的情况下,DI在开发TDD时很厉害。它内置于 .Net Core,因此这些限制是死胡同。

在您要测试的方法中构建 DbContext 与在代码中实例化依赖项的任何其他具体实例没有什么不同,这意味着您无法使用模拟依赖项进行测试。因此,立即想到的选项是(没有 code/structure 更改):

  • 实施内存中数据提供程序以将 EF 指向单元测试。
  • 设置可在测试 运行 之间恢复的已知状态数据库。 (更多的是集成测试,不太适合 TDD,在 TDD 中您希望可以非常快速、非常频繁地进行测试。)
  • 由于缺少 DI/IoC
  • ,将其放入 "wasn't designed for unit testing" 桶中

模拟 DbContext 很麻烦,但可行。一旦你有了一个 mock-able,我会建议在项目中添加一个像 Autofac 这样的 IoC 容器。我不知道在什么情况下会阻止你引入 IoC 容器,但如果团队担心它是一个全有或全无的重构并且工作量太大,那么我会向他们保证它可以在不破坏现有代码的情况下以对项目进行最小更改的方式添加。除了 DbContext,如果代码不使用 DI/IoC 容器,您打算如何处理其他具体依赖项?您不必一次性关闭 所有 dependencies/controllers,而是逐步改进它们。

一旦容器设置为解析 MVC 控制器,具有默认构造函数的现有控制器将不会受到影响。然后,您可以向容器注册 DbContext,并调整被测控制器以接受构造函数中的上下文。例如,IoC 容器会将 DbContext 生命周期范围设置为 Instance per Request,因此您不需要 using {} 块。从那里您的测试可以提供模拟的 DbContext,而容器管理 Context 生命周期。

关于使用 IoC 容器使单元测试友好 controllers/code 我最近发布了一篇关于使用惰性依赖项 /w Autofac 为具有多个依赖项的 类 编写测试的文章折断。您可以在 https://medium.com/@StevePy/writing-easily-testable-code-with-autofac-lazy-properties-f9c63457c8ce

阅读