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



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 容器。它使测试复杂化,没有附加值。

最新更新