放置所有依赖项的测试仍然很重要,但是正如Jeffrey Faust所说的,它更在集成测试领域。
单元测试最重要的方面之一就是使您的测试值得信赖。如果您不相信通过测试确实意味着一切都很好,而测试失败则意味着生产代码中存在问题,那么您的测试就没有那么有用了。
为了使您的测试值得信赖,您必须做一些事情,但是我将只关注其中一个答案。您必须确保它们易于运行,以便所有开发人员都可以在签入代码之前轻松地运行它们。“易于运行”意味着您的测试可以快速运行,并且不需要进行大量配置或设置即可使它们运行。理想情况下,任何人都应该能够签出代码的最新版本,立即运行测试,然后查看它们是否通过。
通过抽象化对其他事物(文件系统,数据库,Web服务等)的依赖关系,可以避免进行配置,并使您和其他开发人员更容易受到以下情况的诱惑:“哦,测试失败了,因为我没有这样做”还没有建立网络共享。哦,好,我稍后再运行。”
如果要测试对某些数据的处理方式,则针对该业务逻辑代码的单元测试不必关心如何获取这些数据。能够测试应用程序的核心逻辑而不必依赖于数据库等支持工具,真是太棒了。如果您不这样做,那么您会错失良机。
PS我应该补充一点,绝对有可能以可测试性为名进行过度设计。测试您的应用程序可以缓解这种情况。但是无论如何,糟糕的方法实现并不会降低方法的有效性。如果有人不停地问“我为什么要这样做?”,任何事情都可能被滥用和过度。在发展中。
就内部依赖而言,事情变得有些混乱。我想考虑的方式是,我想尽可能地保护我的班级,以免因错误的原因而改变。如果我有这样的设置...
public class MyClass
{
private SomeClass someClass;
public MyClass()
{
someClass = new SomeClass();
}
// use someClass in some way
}
我通常不在乎如何SomeClass
创建。我只想使用它。如果SomeClass发生更改,并且现在需要构造函数使用参数,那不是我的问题。我不必更改MyClass来适应这一点。
现在,这仅涉及设计部分。就单元测试而言,我还想保护自己不受其他课程的影响。如果我正在测试MyClass,我希望知道没有外部依赖的事实,那就是SomeClass在某些时候没有引入数据库连接或其他外部链接。
但是,更大的问题是我也知道我的某些方法的结果依赖于SomeClass上某个方法的输出。如果不对SomeClass进行模拟/存根,则可能无法更改需求中的输入。如果幸运的话,我可能可以在测试中组合我的环境,使其能够触发SomeClass的正确响应,但是这样做会给测试带来复杂性并使它们变脆。
重写MyClass以在构造函数中接受SomeClass的实例,使我能够创建一个SomeClass的伪实例,该实例返回我想要的值(通过模拟框架或手动模拟)。在这种情况下,我通常不必引入接口。是否这样做在很多方面都是您的选择所决定的个人选择(例如,在C#中接口的可能性更大,但在Ruby中绝对不需要)。