Mar*_*age 561

序言:如果你在字典中查找名词模拟,你会发现这个单词的一个定义是作为模仿的东西.


模拟主要用于单元测试.被测对象可能依赖于其他(复杂)对象.要隔离要通过模拟真实对象行为的模拟替换其他对象的对象的行为.如果将真实对象合并到单元测试中是不切实际的,这将非常有用.

简而言之,模拟是创建模拟真实对象行为的对象.


有时您可能想要区分嘲弄而不是剔除.关于这个主题可能存在一些分歧,但我对存根的定义是一个"最小"的模拟对象.存根实现了足够的行为以允许被测对象执行测试.

模拟就像一个存根,但测试也将验证被测对象是否按预期调用模拟.部分测试是验证模拟是否正确使用.

举个例子:您可以通过实现用于存储记录的简单内存结构来存根数据库.然后,测试对象可以读取和写入数据库存根的记录,以允许它执行测试.这可以测试与数据库无关的对象的某些行为,并且仅包括数据库存根以使测试运行.

如果您想要验证测试对象是否将某些特定数据写入数据库,则必须模拟数据库.然后,您的测试将包含有关写入数据库mock的内容的断言.

  • 这是一个很好的答案,但它不必要地将模拟的概念限制为*对象*.用"单位"替换"对象"会使它更加通用. (15认同)

Hon*_*ney 75

其他答案解释了什么是嘲弄.让我通过一个例子引导你.相信我,它实际上比你想象的要简单得多.

tl; dr它是原始类的子类.它还注入了其他数据,因此您可以避免测试注入的部分,并专注于测试其余代码.


假设您正在编写iOS应用程序并进行网络调用.您的工作就是测试您的应用程序.测试/识别网络呼叫是否按预期工作不是您的责任.测试它是另一方(服务器团队)的责任.您必须删除此(网络)依赖项,然后继续测试所有围绕它的代码.

网络呼叫可以使用JSON响应返回不同的状态代码404,500,200,303等.

您的应用程序可能适用于所有这些应用程序(如果出现错误,您的应用程序应该抛出预期的错误).你用mocking做的是你创建'虚构 - 类似于真实的'网络响应(比如带有JSON文件的200代码)并测试你的代码而不用 "进行真正的网络调用并等待你的网络响应".您手动对所有类型的网络响应进行硬编码/返回网络响应,并查看您的应用是否按预期工作.(你从不假设/测试200不正确的数据,因为这不是你的责任,你的责任是用正确的200 测试你的应用程序,或者在400,500的情况下,你测试你的应用程序是否抛出正确的错误)

这创造虚构 - 类似于真实被称为嘲弄.

为此,您无法使用原始代码(您的原始代码没有预先插入的响应,对吧?).您必须向其添加内容,注入/插入通常不需要的虚拟数据(或类的一部分).

因此,您将原始类类化并添加所需的任何内容(此处为网络HTTPResponse,数据OR,如果失败,您传递正确的errorString,HTTPResponse),然后"测试子类"即模拟类.
您不再测试原始课程.模拟/子类代表原始类进行测试

长话短说,嘲笑是为了简化限制你正在测试的东西,也让你按照课程所依赖的方式提供.在这个例子中,你避免测试网络电话本身,而是考验你希望你的应用程序是否适用与注入输出/响应 -通过嘲讽

不用说,您可以单独测试每个网络响应.


现在,我一直想到的一个问题是:合同/终点以及我的API的JSON响应基本上都会不断更新.如何编写考虑到这一点的单元测试?

详细说明:假设模型需要一个名为的键/字段username.你测试这个,你的测试通过.2周后,后端将密钥的名称更改为id.你的测试仍然通过.对?或不?

后端开发人员是否有责任更新模拟.我们的协议是否应该提供更新的模拟?

上述问题的答案是:单元测试+作为客户端开发人员的开发过程应该/将捕获过时的模拟响应.如果你问我怎么样?答案是:

如果没有使用更新的API,我们的实际应用程序将失败(或者没有失败但没有所需的行为)...因此,如果失败...我们将对我们的开发代码进行更改.这再次导致我们的测试失败....我们将不得不纠正它.(实际上,如果我们要正确地执行TDD过程,我们不会写任何关于该字段的代码,除非我们为它编写测试...并看到它失败然后去为它编写实际的开发代码.

这一切都意味着后端不必说:"嘿,我们更新了模拟"......它最终通过代码开发/调试发生.因为它是开发过程的全部内容!虽然如果后端为您提供模拟响应,那么它会更容易.

我的全部观点是,提出一些脚本来获取API的更新模拟效率很低.TDD也没有将100%的努力放在自动完成任务上,而没有任何人为干预.部分过程是手动更新JSON并召开简短会议以确保其值是最新的.

本节的编写归功于我们的CocoaHead聚会小组的讨论


仅适用于iOS开发人员:

一个非常好的嘲弄的例子是Natasha Muraschev的这个实用的协议导向谈话刚刚跳到分钟18:30.

我真的很喜欢成绩单中的这一部分:

因为这是测试...我们确实希望确保调用该get函数Gettable,因为它可以返回,理论上该函数可以从任何地方分配食物项目数组.我们需要确保它被调用;

  • 很棒的例子,我只想补充一点,在这个特定的例子中,子类充当了模拟,但这个例子也使用了存根。硬编码的JSON响应被视为存根响应。我之所以仅添加它,是因为很难区分模拟和存根,但是此示例清楚地说明了如何将它们一起使用。 (2认同)

Dav*_*all 31

SO上有很多答案,网上有关于嘲笑的好帖子.你可能想要开始寻找的一个地方是Martin Fowler Mocks Are Not Stubs的帖子,他讨论了许多嘲笑的想法.

在一个段落中 - Mocking是一种特殊技术,允许测试代码单元而不依赖于依赖性.通常,模拟与其他方法的不同之处在于,用于替换代码依赖关系的模拟对象将允许设置期望 - 模拟对象将知道代码如何调用它以及如何响应.


你的原始问题提到了TypeMock,所以我在下面给出了答案:

TypeMock是商业模拟框架的名称.

它提供了免费模拟框架(如RhinoMocks和Moq)的所有功能,以及一些更强大的选项.

你是否需要TypeMock是值得商榷的 - 你可以用免费的模拟库做大多数你想要的模拟,而且很多人认为TypeMock提供的功能通常会让你远离完美的封装设计.

正如另一个答案所说'TypeMocking'实际上并不是一个定义的概念,但可以理解为TypeMock提供的模拟类型,使用CLR分析器在运行时拦截.Net调用,提供更大的假对象能力(不是要求)例如需要接口或虚拟方法).

  • @Peter - 正如另一条评论所说,检查问题的编辑历史.如果我发布答案然后原始问题完全改变,我可以做很多事情. (4认同)

Ven*_*tra 9

Mock是一种方法/对象,以受控方式模拟真实方法/对象的行为.模拟对象用于单元测试.

通常,测试下的方法会调用其中的其他外部服务或方法.这些被称为依赖项.一旦被模拟,依赖关系就像我们定义它们一样.

由于依赖项由模拟控制,我们可以轻松地测试我们编码的方法的行为.这是单元测试.

模拟对象的目的是什么?

模拟vs存根

单元测试与功能测试


Bri*_*sen 5

模拟类型的目的是切断依赖关系,以便将测试隔离到特定单元.存根是简单的代理,而模拟是可以验证使用的代理.模拟框架是一种可以帮助您生成存根和模拟的工具.

编辑:由于原来的措辞提到"类型嘲笑"我得到的印象,这与TypeMock有关.根据我的经验,一般术语只是"嘲弄".请随意忽略以下有关TypeMock的信息.

TypeMock Isolator与大多数其他模拟框架的不同之处在于它可以动态修改IL.这允许它模拟大多数其他框架无法模拟的类型和实例.要使用其他框架模拟这些类型/实例,您必须提供自己的抽象并模拟这些.

TypeMock以干净的运行时环境为代价提供了极大的灵活性.作为TypeMock实现其结果的方式的副作用,使用TypeMock时有时会得到非常奇怪的结果.


小智 5

模拟正在生成模拟真实对象行为以进行测试的伪对象