我已经阅读了多篇关于测试中模拟和存根(stubbing)的文章,包括Martin Fowler的《Mocks Aren't Stubs》,但仍然不理解它们之间的区别。
我已经阅读了多篇关于测试中模拟和存根(stubbing)的文章,包括Martin Fowler的《Mocks Aren't Stubs》,但仍然不理解它们之间的区别。
有几种并非真实的对象定义,它们的通用术语是测试替身。这个术语包括:虚假对象、伪造对象、存根和模拟对象。
- 虚拟对象只是在方法参数列表中占位而从未被实际使用过。
- 伪造对象实际上具有有效的实现,但通常采取了一些捷径,使它们不适用于生产环境(内存数据库是一个很好的例子)。
- 存根为测试期间进行的调用提供预设的答案,通常不响应任何程序外部的请求。存根也可以记录有关调用的信息,例如记住发送的消息的电子邮件网关存根,或仅记住它“发送”的消息数量。
- 模拟对象是我们在这里正在讨论的:预编程的对象,其期望形成对其接收到的调用的规范。
模拟对象 vs 存根 = 行为测试 vs 状态测试
根据每个测试只测试一个事物原则,一个测试中可能会有几个存根,但通常只有一个模拟对象。
使用存根的测试生命周期:
使用模拟对象的测试生命周期:
Mocks和Stubs测试都可以回答问题:结果是什么?
使用Mocks进行测试还关注:结果是如何实现的?
桩
我认为最大的区别在于,桩是具有预定行为的已经编写好的代码。因此,您将拥有一个实现依赖项(很可能是抽象类或接口)的类,用于测试目的,方法只是用设置响应来进行桩处理。它们不会做任何花哨的事情,而且您已经在测试之外编写了这些桩代码。
模拟对象
模拟对象是您在测试中必须根据您的期望设置的内容。模拟对象并未以预定方式设置,因此您需要在测试中编写代码来完成此项工作。某种程度上说,模拟对象是在运行时确定的,因为设置预期的代码必须在它们执行任何操作之前运行。
模拟对象和桩之间的区别
使用模拟对象编写的测试通常遵循“初始化 -> 设置预期 -> 执行 -> 验证”模式进行测试。而预先编写的桩将遵循“初始化 -> 执行 -> 验证”模式。
模拟对象和桩之间的相似之处
两者的目的都是消除类或函数的所有依赖项的测试,使您的测试更加专注和简单,以证明它们试图证明的内容。
桩对象是一种简单的伪造对象,其作用是确保测试正常运行。
模拟对象是一种更智能的桩对象。您可以通过它验证测试是否通过。
以下是每个的描述,以及实际世界示例。
API
而创建的虚假值。示例:如果您正在测试一个类的方法,该方法在构造函数中需要许多强制参数,但这些参数对您的测试没有影响,那么您可以创建虚拟对象来创建类的新实例。
示例:为访问数据库创建伪造实现,将其替换为
内存中的
集合。
基于状态
。Stub
用指定结果的代码替换方法。
Mock
所以,正如Sean Copenhaver在他的答案中描述的那样,区别在于mock设置期望(即对它们是否以及如何被调用进行断言)。带有断言方法是否被调用的存根。
存根不会失败你的测试,模拟可以。
阅读上面的所有解释,让我来简化一下:
判断我们正在处理存根(Stub)的最简单的方法是注意到存根永远不可能使测试失败。测试使用的断言(asserts)始终针对被测类。
另一方面,测试将使用模拟对象来验证测试是否失败。[...]
同样,模拟对象就是我们用来查看测试是否失败的对象。
存根和模拟都是假的(fake)。
如果您对存根进行断言,则意味着您将存根用作模拟;如果您仅使用存根运行测试而没有对其进行断言,则表示您将存根用作存根。
Mock是测试行为的一种方式,确保调用了某些特定的方法。
Stub是一个特定对象的可测试版本。
"苹果式"是什么意思?
如果你把它与调试进行比较:
存根就像确保方法返回正确的值。
Mock就像实际进入方法,并确保返回正确的值之前,内部所有内容都是正确的。