单元测试中的模拟与 IoC 容器

Mocking vs IoC container in unit testing

TL;DR - 我把 "Integration Tests" 和 "Unit Tests" 搞混了。

我对单元测试和 IoC 容器感到困惑...:(

我读过 this article 关于您不应该在单元测试中使用 IoC 容器的内容。这似乎是许多人对 SO 和其他各种文章的看法。在单元测试中,您测试您的方法,但任何依赖项都应该被模拟。

利用上述文章,想请教一些问题

To put it another way, if component A calls component B then from a unit testing perspective, we cannot let component A call the actual implementation of component B. Instead, component B must be mocked.

但是……为什么?

We use a fake instead of the real component B so that 1) our tests do not rely on code in any other class, 2) component B returns the same data every time and 3) we can intercept calls to component B so we can check how and when it is being called.

广告。 1) 我现在没有进行测试,而是在实际应用程序中会发生什么情况,我在毫无理由地伪造组件 B...。为了什么目的?这样我就知道组件 A 是以隔离方式进行测试的?但是我的应用程序同时使用了这两个组件,并且这些组件协同工作。

引文暗示我必须单独对组件 A 和组件 B 进行单元测试,并且我应该只测试组件的业务。

但这破坏了自动化测试的全部意义,在自动化测试中,我创建了关于应用程序功能的保证,应用程序不会崩溃,同时使用这两个组件。与孤立上下文中的内部单元无关。

广告。 2) 我知道我测试的所有内容都是确定性的,对于各种输入 X,它会 return 一些 Y,或者抛出异常或其他东西 - 这就是我实际测试的内容。

广告。 3) 我可以想象这在复杂的测试中是有意义的...

对我来说,如果组件 B 是第 3 方代码,我无法在不复制大量代码的情况下轻松地在测试中创建 class...或者如果我有理由不调用组件 B 的实际实现,例如不是真的想在数据库中进行实际更改,实际上不是发送电子邮件,实际上不是 moving/writing/reading/deleting 文件等

但是,我会使用不同的 IoC 容器进行模拟,而不是 Bind<ISomeService>().To<BusinessImplementation>() 我会写 Bind<ISomeService>().To<TestImplementation()(Ninject 中的代码示例)

通过测试,我想对应用程序、部署的应用程序中会发生什么以及通过无缘无故地模拟依赖项做出保证,我在一个非常不同的上下文中进行测试。

当应用程序启动时,它使用我编写的 IoC 容器。使用 IoC 容器解决应用程序的依赖关系。


我相信我可能在某些事情上是错误的,但我还看不到...

目的不是要替换将从更高级别测试模块的集成测试。单元测试旨在单独测试离散 class,主要是为了确认 class 的设计和编码是否完整。

Instead, component B must be mocked. But... why?

简单地说,单元测试就是测试一个单一的特性。如果测试失败,是因为组件A还是组件B?

考试内容是什么?

组件 B 是否通过了自己的一系列测试?

测试组件 A 和 B 的真实实例不会回答这些问题。相反,它会提出比实际回答更多的问题。

Instead of testing, what would happen in the real application, I'm now reasonlessly falsificating component B....to what end?

实际上,它是将组件 A 与组件 B 隔离开,这样任何异常行为都只会由组件 A 引起。这是为了降低测试的复杂性并明确您在做什么,因此 "unit" 在单元测试中。

To me mocking makes sense if component B is a 3rd party code I cannot easily create in a testing class without duplicating an awful lot of a code...

基本上,你可以这样做。这不仅在单元测试中很重要。相反,使用依赖注入,应该模拟每个引用类型,以便将组件 A 与任何外部影响隔离开来,即确保组件 A 的行为符合预期。

你想用组件B的真实实例来测试组件A的那一天其实不是你做单元测试的那一天,但这叫做集成测试,那些测试应该在你确定每个组件以单一形式运行。

请看这个问题的答案:Unit Testing or Functional Testing?

附带说明一下,不建议在单元测试中使用 DI 容器。它使测试复杂化,没有附加值。