TL;DR - 我将"集成测试"与"单元测试"混为一谈。
我对单元测试和 IoC 容器感到困惑... :(
我已经阅读了这篇文章,介绍如何在单元测试中使用 IoC 容器。这似乎是许多人对SO和其他各种文章的看法。在单元测试中,测试方法,但应模拟任何依赖项。
使用上述文章,我想问一些问题。
换句话说,如果组件 A 调用组件 B,那么从单元测试的角度来看,我们不能让组件 A 调用组件 B 的实际实现。相反,组件 B 必须被模拟。
但。。。为什么?
我们使用假组件而不是真正的组件 B,以便 1) 我们的测试确实 不依赖于任何其他类中的代码,2)组件 B 返回相同的 每次都有数据,3)我们可以拦截对组件B的调用,这样我们就可以 检查调用方式和时间。
广告 1)我现在没有测试实际应用程序中会发生什么,而是合理地伪造组件 B......为了什么目的?所以我知道组件 A 是以孤立的方式测试的?但是我的应用程序同时使用这两个组件,并且这些组件协同工作。
引用意味着我必须单独对组件 A 和组件 B 进行单元测试,并且我应该只测试组件的业务。
但这破坏了自动化测试的全部意义,在自动化测试中,我创建了有关应用程序功能的保证,即应用程序不会崩溃,同时使用这两个组件。不是关于孤立上下文中的内部单位。
广告2)我知道我测试的所有内容都是确定性的,对于各种输入 X,它将返回一些 Y,或者抛出异常或其他东西 - 这就是我实际测试的内容。
广告3)我可以想象这在复杂的测试中是有意义的......
对我来说,如果组件 B 是 3rd 方代码,那么在不复制大量代码的情况下,我无法在测试类中轻松创建,那么嘲笑是有意义的......或者,如果我有理由不调用组件 B 的实际实现,例如不是真的想在数据库中进行实际更改,实际上没有发送电子邮件,实际上没有移动/写入/读取/删除文件等。
但是,我会使用不同的 IoC 容器进行模拟,而不是Bind<ISomeService>().To<BusinessImplementation>()
我会编写Bind<ISomeService>().To<TestImplementation()
(Ninject 中的代码示例)
通过测试,我想对应用程序、已部署应用程序中会发生什么做出保证,并通过在没有充分理由的情况下嘲笑依赖项,我在非常不同的上下文中进行测试。
当应用程序启动时,它使用我编写的 IoC 容器。应用程序的依赖项使用 IoC 容器解析。
我相信我可能错了什么,但我还看不出来......
目的不是替换将从更高级别测试模块的集成测试。单元测试旨在单独测试离散类,主要是为了确认该类的设计和编码是完整的。
相反,组件 B 必须被模拟。但。。。为什么?
简单地说,单元测试是用来测试一个单一的功能。如果测试失败,是因为组件A还是组件B?
测试是关于什么的?
组件 B 是否通过了自己的测试?
测试组件 A 以及 B 的真实实例将无法回答这些问题。相反,它会产生比它实际能够回答的更多的问题。
我现在没有测试实际应用程序中会发生什么,而是合理地伪造组件 B......为了什么目的?
实际上,它是将组件A与组件B隔离开来,以便任何不当行为仅由组件A引起。这是为了降低测试的复杂性,并明确你在做什么,因此单元测试中的"单元"。
对我来说,如果组件 B 是 3rd 方代码,那么在不复制大量代码的情况下,我无法在测试类中轻松创建,那么嘲笑是有意义的......
基本上,你可以这样做。这不仅是单元测试中的重要内容。相反,使用依赖注入,应该模拟每个引用类型,以便将组件 A 与任何外部影响隔离开来,也就是说,确保组件 A 的行为符合预期。
你想用组件B的真实实例测试组件A的那一天实际上不是你进行单元测试的那一天,但这被称为集成测试,这些测试应该在你确定每个组件都以酉形式运行之后编写。
请参阅此问题的答案:单元测试还是功能测试?
附带说明一下,不建议在单元测试中使用 DI 容器。它使测试复杂化,没有附加值。