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

时间:2015-01-20 18:49:45

标签: c# unit-testing dependency-injection automated-tests ioc-container

TL; DR - 我将“集成测试”与“单元测试”混为一谈。

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

我已阅读this article有关如何在单元测试中不使用IoC容器的信息。这似乎是许多人对SO和其他各种文章的看法。在单元测试中,您可以测试您的方法,但应该模拟任何依赖项。

使用上述文章,我想问一些问题。

  

换句话说,如果组件A从单元测试角度调用组件B,我们不能让组件A调用组件B的实际实现。相反,组件B必须被模拟。

但是......为什么?

  

我们使用假的而不是真实的组件B,以便1)我们的测试   不依赖于任何其他类中的代码,2)组件B返回相同的内容   每次数据和3)我们可以拦截对组件B的调用   检查它是如何以及何时被调用的。

广告。 1)而不是测试,在实际应用程序中会发生什么,我现在无理地伪造组件B ....到底是什么?所以我知道组件A是以孤立的方式进行测试的吗?但是我的应用程序将两个组件一起使用,这些组件一起工作。

引用意味着我必须单独测试组件测试组件A和组件B,并且我应该只测试组件的业务。

但这破坏了自动化测试的全部要点,在这一点上,我使用这两个组件一起创建了关于应用程序功能的保证,应用程序不会崩溃。不是孤立背景下的内部单位。

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

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

对我来说,模拟是有意义的,如果组件B是第三方代码,我不能轻易地在测试类中创建而不会复制大量的代码......或者如果我有理由不调用组件B的实际实现因为不是真的想要在数据库中进行实际更改,而不是实际发送电子邮件,而不是实际移动/写入/读取/删除文件等。

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

通过测试,我想对应用程序做出保证,在部署的应用程序中会发生什么,并且在没有充分理由的情况下模拟依赖关系,我在一个非常不同的上下文中进行测试。

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


我相信我可能对某些事情不对,但我还看不到......

2 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:1)

  

相反,组件B必须被模拟。但是......为什么?

简单地说,单元测试用于测试单一的功能。如果测试失败,是因为组件A还是组件B?

测试的内容是什么?

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

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

  

而不是测试,在实际应用程序中会发生什么,我现在无理地伪造组件B ......到底是什么?

实际上,它是将组件A与组件B隔离,这样任何行为异常都只是由组件A引起的。这样可以降低测试的复杂性,并使其清楚你正在做什么,因此&#34;单元&# 34;在单元测试中。

  

对我来说,如果组件B是第三方代码,那么模拟是有道理的,我不能轻易地在测试类中创建而不会复制大量的代码......

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

您希望使用组件B的实际实例测试组件A的那一天实际上不是您进行单元测试的那一天,但这称为集成测试,并且在您确定每个组件的行为之后应编写这些测试以统一的形式。

请参阅此问题的答案:Unit Testing or Functional Testing?

另外,建议不要在单元测试中使用DI容器。它使测试变得复杂,没有附加价值。