关于单元测试:什么是Mocking?

What is Mocking?

什么是嘲笑?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????.


序言:如果你查字典里的名词模拟,你会发现这个词的一个定义是模仿的。

模拟主要用于单元测试。正在测试的对象可能依赖于其他(复杂)对象。要隔离对象的行为,您需要用模拟真实对象行为的模拟来替换其他对象。如果实际对象不适合合并到单元测试中,这是有用的。

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

有时,你可能想区分嘲笑和存根。关于这个主题可能有些不同,但我对存根的定义是一个"最小"的模拟对象。存根实现了足够的行为以允许被测试对象执行测试。

模拟类似于存根,但测试还将验证被测对象是否按预期调用了模拟。测试的一部分是验证模型是否正确使用。

举个例子:通过实现一个简单的内存结构来存储记录,您可以截取一个数据库。然后,被测试对象可以读写记录到数据库存根,以允许它执行测试。这可以测试与数据库无关的对象的某些行为,并且将包含数据库存根,以便让测试运行。

如果您想验证被测对象是否向数据库写入了一些特定的数据,那么必须模拟数据库。然后,您的测试将包含关于写入数据库模拟的内容的断言。


其他答案解释了什么是嘲笑。让我用一个例子引导您完成它。相信我,其实比你想象的要简单得多。好的。

它是原始类的一个子类。它注入了其他数据,因此您可以避免测试注入的部分,而只关注测试代码的其余部分。好的。

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

网络调用可以返回不同的状态代码404、500、200、303等,并带有JSON响应。好的。

您的应用程序应该可以为所有这些应用程序工作(如果出现错误,您的应用程序应该抛出其预期的错误)。模拟的方法是创建"假想的类似于真实的"网络响应(比如一个带有JSON文件的200代码),然后在不进行"进行真实的网络调用并等待网络响应"的情况下测试代码。您手动硬编码/返回各种网络响应的网络响应,并查看您的应用程序是否按预期工作。(您从不假设/测试一个有错误数据的200,因为这不是您的责任,您的责任是用正确的200测试您的应用,或者如果是一个400,500,您测试您的应用是否抛出了正确的错误)好的。

这种创造假想类似于真实的被称为嘲弄。好的。

为了做到这一点,您不能使用原始代码(您的原始代码没有预先插入的响应,对吗?)。必须添加一些东西,插入/插入通常不需要的虚拟数据(或类的一部分)。好的。

因此,您对原始类进行子类化,并添加您需要的任何内容(这里是网络httpResponse、数据,或者在失败的情况下,传递正确的errorString、httpResponse),然后"测试子类"(即模拟类)。您不再测试原始类。模拟/子类代表原始类进行测试好的。

长话短说,模拟是为了简化和限制您正在测试的内容,并且使您能够提供类所依赖的内容。在这个例子中,您避免测试网络调用本身,而是通过模拟类来测试您的应用程序在注入的输出/响应中是否如您所期望的那样工作。好的。

不用说,您单独测试每个网络响应。好的。

现在我一直在想的一个问题是:我的API的契约/端点和JSON响应会不断更新。如何编写考虑到这一点的单元测试?好的。

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

后端开发人员是否有责任更新模拟。他们是否应该是我们协议的一部分,提供最新的模拟?好的。

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

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

这意味着后端不必说:"嘿,我们更新了模拟"…它最终会通过代码开发/调试发生。???因为这都是开发过程的一部分!不过,如果后端系统为您提供模拟响应,那么就更容易了。好的。

我的观点是(如果你不能自动获得更新的模拟API响应),需要一些人工交互,比如手动更新JSON,并召开简短的会议以确保它们的值是最新的,这将成为你过程的一部分。好的。

本节的编写归功于我们的Cocoahead Meetup Group中的一个松散讨论好的。

仅限iOS开发者:好的。

模拟的一个很好的例子是Natasha Muraschev的这个面向协议的实用对话,跳到第18:30分钟。好的。

我真的很喜欢这段文字:好的。

Because this is testing...we do want to make sure that the get function
from the Gettable is called, because it can return and the function
could theoretically assign an array of food items from anywhere. We
need to make sure that it is called;

Ok.

好啊。


网上有很多关于模仿的答案和好文章。你可能想开始找的一个地方是马丁·福勒的帖子《嘲弄不是存根》,他讨论了很多嘲弄的想法。

在一个段落中,mocking是一种特殊的技术,它允许在不依赖依赖依赖于依赖性的情况下对代码单元进行测试。一般来说,模拟与其他方法的区别在于,用于替换代码依赖关系的模拟对象将允许设置期望值——模拟对象将知道代码如何调用它以及如何响应。

你最初的问题提到了typemock,所以我把我的答案留在下面:

typemock是商业模拟框架的名称。

它提供了免费模拟框架的所有功能,如Rhinomocks和MoQ,以及一些更强大的选项。

不管你是否需要typemock都是有争议的——你可以用免费的mocking库做你想要做的大多数mocking,许多人认为typemock提供的功能通常会让你远离封装良好的设计。

正如另一个答案所述,"type mocking"实际上不是一个定义的概念,但可以理解为typemock提供的模拟类型,使用clr探查器在运行时拦截.net调用,从而提供更大的伪造对象的能力(而不是需要接口或虚拟方法等要求)。


mock是一种方法/对象,它以受控方式模拟实际方法/对象的行为。模拟对象用于单元测试。

测试中的方法通常调用其他外部服务或其中的方法。这些被称为依赖关系。一旦被嘲笑,依赖关系就按照我们定义的方式工作。

通过mock控制依赖项,我们可以很容易地测试我们编码的方法的行为。这是单元测试。

模拟对象的目的是什么?

嘲笑与存根

单元测试与功能测试


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

编辑:由于最初的措辞提到"类型模仿",我觉得这与类型模仿有关。根据我的经验,一般的说法就是"嘲弄"。请随意忽略typemock上的以下信息。

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

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


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


我认为使用typemock隔离器模拟框架将是typemock。

它是一个生成用于单元测试的模拟的工具,而不需要在考虑IOC的情况下编写代码。


如果模拟涉及到一个网络请求,另一种选择是使用一个真正的测试服务器。您可以使用此服务为测试生成请求和响应。http://testerurl.com/