Testing 什么是模拟,何时使用?

Testing 什么是模拟,何时使用?,testing,mocking,glossary,Testing,Mocking,Glossary,我刚刚读了维基百科上的文章,但我仍然不完全清楚他们的目的。当实际对象过于复杂或不可预测时,它们似乎是由测试框架创建的对象(您100%确定模拟对象的值是什么,因为您完全控制它们) 然而,我的印象是所有的测试都是用已知值的对象来完成的,所以我肯定遗漏了一些东西。例如,在一个课程项目中,我们的任务是一个日历应用程序。我们的测试套件由事件对象组成,我们完全知道它们是什么,因此我们可以测试多个事件对象、各种子系统和用户界面之间的交互。我猜这些都是模拟对象,但我不知道你为什么不这么做,因为没有已知值的对象,

我刚刚读了维基百科上的文章,但我仍然不完全清楚他们的目的。当实际对象过于复杂或不可预测时,它们似乎是由测试框架创建的对象(您100%确定模拟对象的值是什么,因为您完全控制它们)


然而,我的印象是所有的测试都是用已知值的对象来完成的,所以我肯定遗漏了一些东西。例如,在一个课程项目中,我们的任务是一个日历应用程序。我们的测试套件由事件对象组成,我们完全知道它们是什么,因此我们可以测试多个事件对象、各种子系统和用户界面之间的交互。我猜这些都是模拟对象,但我不知道你为什么不这么做,因为没有已知值的对象,你就无法测试系统。

想想客户端和服务器软件的经典案例。要测试客户端,您需要服务器;要测试服务器,您需要客户端。这使得单元测试几乎不可能——不使用mock。如果模拟服务器,则可以单独测试客户端,反之亦然


嘲弄的目的不是复制事物的行为,而是嘲弄。它更像是一个简单的状态机,它的状态变化可以通过测试框架进行分析。因此,客户端模拟可能会生成测试数据,将其发送到服务器,然后分析响应。您期望对特定请求做出特定响应,因此您可以测试是否得到了响应。

模拟对象不仅仅是具有已知值的对象。它是一个与复杂对象具有相同接口的对象,您不能在测试中使用它(如数据库连接和结果集),但它的实现可以在测试中进行控制

有一些模拟框架允许您动态创建这些对象,本质上允许您这样说:让我成为一个对象,方法foo接受int并返回bool。当我通过0时,它应该返回true。然后,您可以测试使用foo()的代码,以确保其正确响应

Martin Fowler有一篇关于嘲弄的好文章:

我同意上面所说的一切,你绝对应该阅读@Lou Franco为你指出的Martin Fowler关于测试双打的优秀文章

任何双重测试(伪测试、存根测试或模拟测试)的主要目的是隔离被测对象,以便您的单元测试只测试该对象(而不是它的依赖项以及它与之协作或交互的其他类型)

可以使用提供对象所依赖的接口的对象来代替实际的依赖关系,这样就可以对某些交互的发生做出预期。这可能很有用,但基于状态的测试与基于交互的测试之间存在一些争议。过度使用模拟预期将导致脆性测试


双重测试的另一个原因是删除对数据库、文件系统或其他类型的依赖关系,这些依赖关系的设置或执行耗时的操作非常昂贵。这意味着您可以将对感兴趣的对象进行单元测试所需的时间减至最少。

以下是一个示例:如果您正在编写填充数据库的代码,则可能需要检查特定方法是否已将数据添加到数据库中

设置用于测试的数据库副本存在一个问题,即如果您假定在调用被测试方法之前没有记录,之后有一条记录,那么您需要将数据库回滚到以前的状态,从而增加了运行测试的开销

如果假设只比以前多了一条记录,那么它可能会与连接到同一数据库的第二个测试人员(甚至是同一代码中的第二个测试人员)冲突,从而导致依赖关系并使测试变得脆弱

mock允许您保持测试相互独立,并且易于设置

这只是一个例子——我相信其他人可以提供更多


在这个话题上,我100%同意其他撰稿人的观点,尤其是马丁·福勒的文章。你可能对我们的书感兴趣,请参阅。它是用Java编写的,但这些想法仍然适用