Answers:
DI使单元测试变得更加容易。但是您仍然可以在没有DI的情况下编写单元测试。在DI普及之前,已经编写了许多单元测试。(当然,其中一些使用的技术与DI相同或非常相似,却不知道它有一个奇特的名字:-)
在学习DI之前,我本人已经使用了很多接口和工厂。实际的工厂类名称可能已从配置文件中读取,或已作为参数传递给SUT。
另一种方法是使用单例(或通常是全局可访问的数据)。是的,我知道一般来说,很多人(包括我自己)都不推荐这样做。它仍然可以是在特定情况下可行的,特别是如果单中包含这是不测试用例特定静态配置数据,但生产和测试环境之间不同。当然,它有其已知的问题,因此,如果可以使用DI,DI会更好。但是通常(例如,在旧系统中)您不能这样做。
谈论哪些问题,有效地使用遗留代码描述了许多使测试覆盖遗留代码的技巧。其中许多都不是很好,也不是长期解决方案。但是,它们使您可以创建本来无法测试的系统的第一个有价值的单元测试……这使您可以开始重构,并最终(除其他外)引入DI。
根据所使用的技术,无需使用DI即可隔离依赖关系。例如,在.NET世界中,Moles使您无需DI模式即可隔离依赖项。
就是说,我相信这些隔离框架是为在代码中具有外部依赖项(文件系统,数据库等)的情况而编写的。也就是说,一个人可以做到这一点并不意味着他或她应该这样做。
依赖注入允许进行单元测试,但是它也允许修改对象的行为而无需更改该对象的代码(打开/关闭原理)。因此,产生的不仅仅是可测试的代码,还有灵活的代码。我通常发现,可维护/灵活的代码与可测试的代码之间存在很大的相关性。
不,依赖注入对于单元测试不是必不可少的。
如果您有一个需要依赖类实例进行某些子处理的类,则依赖注入会有所帮助。代替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
...
}
无需依赖注入即可轻松测试计算部分。