【问题标题】:Injecting dependencies into tests将依赖项注入测试
【发布时间】:2010-02-11 12:44:04
【问题描述】:
通常在使用依赖注入时,单元(和其他)测试负责创建/模拟被测系统的依赖并注入它们。
但是,有时测试本身具有依赖项,或者需要将依赖项注入到它自己无法创建的 SUT 中。例如,在测试与数据库交互的类时,测试需要知道连接字符串和目录名称等,这些不能硬编码,因为它们不一定对运行测试的每个人都相同。
那么,您建议如何通过测试找出这些设置?某些 xUnit 风格的测试框架是否提供了一种将依赖项赋予测试夹具的方法?在运行所有测试之前,测试类是否应该具有您填充的静态属性?测试是否应该忽略 DI 实践并从某个全局位置获取依赖项?其他建议?
【问题讨论】:
标签:
unit-testing
design-patterns
dependency-injection
【解决方案1】:
全自动测试有一个原则:您应该能够从源代码控制存储库中提取所有源代码并简单地运行测试。
鉴于环境(机器)具有正确的安装基础(即编译器、测试框架、相关的数据库引擎等),测试负责在执行测试用例之前设置它们的 Fixture。
这意味着对于数据库,测试应该
- 创建有问题的数据库
- 运行测试
- 在最后一个测试用例之后再次删除数据库
如果由于某种原因你不能这样做,你唯一能做的就是在你的源代码控制系统中有一个配置文件,其中包含你测试环境中所有机器的机器特定条目;例如对于机器 Tst1 连接字符串是一个值,但对于 Tst2 它是另一个值。
这很快就会变得丑陋,因此让测试负责夹具设置和拆卸要容易得多,因为这意味着它们可以简单地使用硬编码值或现场生成的值。
这真的与DI无关......
【解决方案2】:
当您使用单元测试框架进行集成测试时,您实际上并没有 DI 或单元测试问题。
您所拥有的是利用高性能单元测试框架的集成测试。
由于它们是集成测试,因此它们在种类上与单元测试不同。 “独立性”不再重要了。
获得因用户而异的集成测试设置的最佳方法是以最终应用程序获取它们的相同方式获取它们。如果您使用 Java,您可能有一个属性文件。在 Python 中,我们有特殊的 Django 设置文件用于集成测试。
【解决方案3】:
DI 与依赖项的复杂性作斗争,而您的单元测试在大多数情况下必须非常简单。典型的单元测试将检查一个孤立类的一个孤立方面。您创建模拟并(通常)通过 CUT(被测类)构造函数注入它们,而不是其所有依赖项。这里通常不需要 DI 框架。
但是。显然,一些更高级别的测试可能仍然需要非模拟依赖项。例如,您想对大量数据进行测试,并且不想创建特殊的假数据源,因此将其保存在真实数据库中(也许您还使用该数据进行一些 UI 测试)。在那种情况下,我仍然会尽量保持简单,在类设置/测试设置方法中初始化测试。
你看,你需要在这里小心。每当您进行大型、复杂的测试时:
- 创建额外的复杂代码,这需要支持。
- 创建一个没有明确失败原因的测试。它可能由于当天的连接不良而失败。你不能依赖它的结果。
- 创建一个无法轻松快速运行的测试,例如在签到时。更少的人会运行它,就会有更多的错误发生。
等等……