正如投票最多的答案所提到的,Martin Fowler在《Mocks不是存根》中讨论了这些区别,特别是副标题《Mocks和存根之间的区别》,所以一定要阅读那篇文章。
与其关注这些东西有何不同,我认为关注为什么这些是不同的概念更有启发性。每一种都有不同的目的。
假货
fake是一种行为“自然”,但不是“真实”的实现。这些都是模糊的概念,所以不同的人对什么是假的有不同的理解。
一个假的例子是内存中的数据库(例如使用sqlite和:memory: store)。您永远不会在生产中使用它(因为数据不是持久化的),但是作为一个数据库在测试环境中使用它是完全足够的。它也比“真正的”数据库轻量级得多。
作为另一个例子,也许您在生产中使用某种对象存储(例如Amazon S3),但是在测试中您可以简单地将对象保存到磁盘上的文件;那么您的“保存到磁盘”实现将是一个假的。(或者您甚至可以通过使用内存文件系统来代替“保存到磁盘”操作。)
作为第三个例子,想象一个提供缓存API的对象;如果一个对象实现了正确的接口,但根本不执行缓存,但总是返回缓存失败,那么这个对象就是一种假的。
pseudo的目的不是为了影响被测系统的行为,而是为了简化测试的实现(通过删除不必要的或重量级的依赖项)。
存根
存根是一种行为“不自然”的实现。它是预先配置的(通常由测试设置),以响应具有特定输出的特定输入。
存根的目的是让测试中的系统进入特定的状态。例如,如果您正在为一些与REST API交互的代码编写测试,那么您可以使用一个始终返回固定响应的API来stub REST API,或者使用一个响应带有特定错误的API请求。通过这种方式,您可以编写关于系统如何对这些状态做出反应的断言测试;例如,如果API返回404错误,测试用户得到的响应。
存根通常被实现为只响应您告诉它响应的确切交互。但是使某些东西成为存根的关键特性是它的目的:存根是关于设置您的测试用例的。
模拟
mock类似于存根,但是添加了验证。mock的目的是断言被测系统如何与依赖项交互。
例如,如果您正在为一个向网站上传文件的系统编写测试,您可以构建一个接受文件的模拟,并且可以使用它来断言上传的文件是正确的。或者,在较小的范围内,通常使用对象的模拟来验证被测系统是否调用了被模拟对象的特定方法。
模拟与交互测试绑定在一起,这是一种特定的测试方法。那些更喜欢测试系统状态而不是系统交互的人会谨慎地使用模拟。
测试双打
假的、存根的和模拟的都属于测试替身的范畴。测试double是您在测试中使用的任何对象或系统,而不是其他东西。大多数自动化软件测试都涉及到使用某种类型的测试替身。其他类型的测试双重对象包括虚值、间谍和I/O黑洞。