依赖注入对单元测试至关重要吗?


55

使用依赖注入(DI)对于单元测试至关重要吗?

我想不出另一种方法来隔离代码,因此可以对其进行测试。另外,我见过的所有示例都使用此模式。是因为这是唯一可行的选择,还是还有其他选择?


5
依赖注入不是必需的,但是更广泛的控制反转概念是必需的。
Jeremy Heiler 2012年

这里要说些什么。如果我的代码基数很少,则DI可能没有用。
JB King

@JBKing如果您的代码库很小,则不需要层或单元测试
Sklivvz 2014年

1
要使您的代码可测试,许多设计决策都是必需的。开始编写测试并找出答案。
内森·库珀

Answers:


42

DI使单元测试变得更加容易。但是您仍然可以在没有DI的情况下编写单元测试。在DI普及之前,已经编写了许多单元测试。(当然,其中一些使用的技术与DI相同或非常相似,却不知道它有一个奇特的名字:-)

在学习DI之前,我本人已经使用了很多接口和工厂。实际的工厂类名称可能已从配置文件中读取,或已作为参数传递给SUT。

另一种方法是使用单例(或通常是全局可访问的数据)。是的,我知道一般来说,很多人(包括我自己)都不推荐这样做。它仍然可以是在特定情况下可行的,特别是如果单中包含这是不测试用例特定静态配置数据,但生产和测试环境之间不同。当然,它有其已知的问题,因此,如果可以使用DI,DI会更好。但是通常(例如,在旧系统中)您不能这样做。

谈论哪些问题,有效地使用遗留代码描述了许多使测试覆盖遗留代码的技巧。其中许多都不是很好,也不是长期解决方案。但是,它们使您可以创建本来无法测试的系统的第一个有价值的单元测试……这使您可以开始重构,并最终(除其他外)引入DI。


商定,DI对于模拟对象特别有用。但是在许多情况下,DI在测试中无用。
Kemoda 2012年

例如@Kemoda是什么?
2012年

@VJovic例如独立对象。我想到了一种方法,该方法需要一些参数并执行一些操作,但不依赖于其他组件(因此不需要DI)。
Kemoda 2012年

@Kemoda听起来您正在描述函数式编程,并且您正在使用DI。您将依赖项作为方法参数注入。
Erik Dietrich 2012年

3
@huggie,为什么实施细节会在这里泄漏?注入的依赖项通常隐藏在接口的后面,其重点是客户端类不知道-也不关心-该依赖项的实际实现是真正的生产类还是模拟类。它的实例化发生在客户端类之外,它只能看到现成的实例。
彼得Török

56

去耦对于单元测试至关重要。DI是实现去耦的好方法。


17
一个非常真实的说法,绝不能回答问题。
特拉维斯

10

根据所使用的技术,无需使用DI即可隔离依赖关系。例如,在.NET世界中,Moles使您无需DI模式即可隔离依赖项。

就是说,我相信这些隔离框架是为在代码中具有外部依赖项(文件系统,数据库等)的情况而编写的。也就是说,一个人可以做到这一点并不意味着他或她应该这样做。

依赖注入允许进行单元测试,但是它也允许修改对象的行为而无需更改该对象的代码(打开/关闭原理)。因此,产生的不仅仅是可测试的代码,还有灵活的代码。我通常发现,可维护/灵活的代码与可测试的代码之间存在很大的相关性。


3
或Moles让您认为您已经获得了整洁,可分解的可测试代码,因为您正在通过魔术进行测试。
Wyatt Barnett 2012年

@WyattBarnett是的,非常正确。痣具有使人畏缩的能力,可以使某人说“谁需要这些多态性和打开/关闭的东西,无论如何?!?”
Erik Dietrich 2012年

1
Moles被伪造品所取代,在伪造品页面上,“ Fakes框架可帮助开发人员在其单元测试中创建,维护和注入伪造的实现”对我来说听起来像DI。
签名

1
@Sign您的链接还说:“ Fakes框架可用于填充任何.NET方法,包括密封类型的非虚拟和静态方法。” 隔离框架可以与DI结合使用的事实并不意味着它们就是 DI。
Erik Dietrich 2012年

4

不,DI对于单元测试不是必不可少的,但它会有所帮助。

您可以像使用DI一样使用工厂或定位器进行测试(只是不那么优雅,并且需要更多设置)。

同样,模拟对象在遗留系统中也很重要,在遗留系统中,许多调用被委派给函数而不是依赖项。(模拟对象也可以在适当的设置中广泛使用)

可能存在几乎不可能进行测试的设置。但这不是基于是否使用依赖注入。


3

,依赖注入对于单元测试不是必不可少的。

如果您有一个需要依赖类实例进行某些子处理的类,则依赖注入会有所帮助。代替DI,您可以将业务方法的逻辑分为一个数据收集部分(不可单元测试)和一个可进行单元测试的计算部分。

示例(使用DI)此实现取决于Employee,Account,...

 bool hasPermissionToTransferMoney(Employee employee, Account from, Account to, Money amount)
 {
     if (amount > 100 && employee.isStudent())
        return false;
     if (to.getOwner().getFamiliyName() == employee.getFamilyName() && ...
        return false; // cannot transfer money to himself;
     ...
 }

在分离数据收集和计算之后:

 bool hasPermissionToTransferMoney(Employee employee, Account from, Account to, Money amount)
 {
     return hasPermissionToTransferMoney(employee.isStudent(), employee.getFamilyName(), to.getOwner().getFamilyName(), ...);
 }

 // the actual permission calculation
 static bool hasPermissionToTransferMoney(boolean isStudent, string employeeFamilyName, string receiverFamilyName, ...)
     if (amount > 100 && isStudent)
        return false;
     if (receiverFamilyName == employeeFamiliyName && ...
        return false; // cannot transfer money to himself
     ...
 }

无需依赖注入即可轻松测试计算部分。


1
  • 依赖注入对于单元测试不是必不可少的

  • 另一方面,当您想将一个实现换成另一个实现时,控制反转至关重要。


0

是的,使用DI进行隔离还有其他选择。

一种替代方法是使用工厂或ServiceLocator,可以从测试中对其进行配置以返回模拟对象而不是真实对象。

另一个是使用合适的隔离框架或模拟工具。这些工具几乎适用于每种现代编程语言(至少适用于Java,C#,Ruby和Python)。他们可以将正在测试的类与其他类/类型的实现隔离开来,即使被测试的类直接实例化其依赖关系也是如此。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.