我知道如何使用这些术语,但是我想知道是否存在接受伪造,模拟和存根的单元测试定义?您如何为测试定义这些?描述您可能会使用每种情况的情况。
这是我的使用方式:
Fake :实现接口但包含固定数据且没有逻辑的类。只需根据实现返回 “好” 或 “坏” 数据。
Mock :一个实现接口的类,该类允许动态设置要返回的值 / 从特定方法抛出的异常,并提供检查是否已调用 / 未调用特定方法的能力。
存根(Stub) :类似于模拟类,不同之处在于它不提供验证方法是否已被调用的能力。
模拟和存根可以手动生成,也可以由模拟框架生成。伪类是手工生成的。我主要使用模拟来验证我的类和依赖类之间的交互。一旦验证了交互作用并正在测试代码中的替代路径,便会使用存根。我主要使用伪造的类来抽象出数据依赖关系,或者当模拟 / 存根过于繁琐而无法每次设置时。
您可以获得一些信息:
伪对象实际上具有有效的实现,但是通常采取一些捷径,这使它们不适合生产
存根提供对测试期间进行的呼叫的固定答复,通常通常根本不响应为测试编程的内容。存根还可以记录有关呼叫的信息,例如,电子邮件网关存根可以记住 “已发送” 的消息,或者仅记住 “已发送” 的消息数量。
嘲笑是我们在这里谈论的话题:对象被预编程并带有期望,形成了期望接收的呼叫的规范。
伪造的:我们获取或构建一个非常轻量级的功能,实现与 SUT 所依赖的组件所提供的功能相同的功能,并指示 SUT 使用它而不是实际的功能。
存根:此实现被配置为使用将在 SUT 中执行未经测试的代码(请参阅第 X 页的生产错误)的值(或异常)响应来自 SUT 的调用。使用测试存根的一个关键指示是由于无法控制 SUT 的间接输入而导致出现未经测试的代码
模拟对象,实现与 SUT(被测系统)所依赖的对象相同的接口。当我们需要进行行为验证时,可以将模拟对象用作观察点,以避免因无法观察调用方法对 SUT 的副作用而导致未经测试的需求(请参阅第 X 页的生产错误)。
我尝试通过使用简化:模拟和存根。当它是一个返回设置为测试类的值的对象时,我会使用 Mock。我使用 Stub 模仿要测试的 Interface 或 Abstract 类。实际上,您所说的并不重要,它们都是生产中不使用的类,并且用作测试的实用程序类。
存根- 提供对方法调用的预定义答案的对象。
模拟- 您在其上设定期望的对象。
伪造品- 功能有限(出于测试目的)的对象,例如伪造的 Web 服务。
Test Double 是存根,模拟和伪造品的总称。但是非正式地,您经常会听到人们简单地称他们为嘲笑。
让我感到惊讶的是,这个问题已经存在了很长时间,而且还没有人根据Roy Osherove 的 “单元测试的艺术”提供答案。
在 “3.1 介绍存根” 中,将存根定义为:
存根是系统中现有依赖项(或协作者)的可控替代。通过使用存根,您可以测试代码而无需直接处理依赖项。
并将存根和模拟之间的区别定义为:
关于模拟与存根之间要记住的主要事情是,模拟就像存根一样,但是您针对模拟对象断言,而您不针对存根进行断言。
伪造品只是用于存根和模拟的名称。例如,当您不关心存根和模拟之间的区别时。
Osherove 区分存根和模拟的方式,这意味着用作测试伪造品的任何类都可以是存根或模拟。特定测试的内容完全取决于您如何在测试中编写检查。
使用 FakeX 类作为存根的测试示例:
const pleaseReturn5 = 5;
var fake = new FakeX(pleaseReturn5);
var cut = new ClassUnderTest(fake);
cut.SquareIt;
Assert.AreEqual(25, cut.SomeProperty);
fake
实例用作存根,因为Assert
根本不使用fake
。
使用测试类 X 作为模拟的测试示例:
const pleaseReturn5 = 5;
var fake = new FakeX(pleaseReturn5);
var cut = new ClassUnderTest(fake);
cut.SquareIt;
Assert.AreEqual(25, fake.SomeProperty);
在这种情况下, Assert
在检查一个值fake
,使得该假一模拟。
现在,当然,这些示例是非常人为设计的,但是我看到这种区别有很大的优点。它使您知道如何测试自己的东西以及测试的依赖关系在哪里。
我同意 Osherove 的观点
从纯粹的可维护性角度来看,在我的测试中,使用模拟比不使用模拟会带来更多的麻烦。那是我的经验,但是我一直在学习新的东西。
断言是您真正要避免的事情,因为它使您的测试高度依赖于完全不是被测类的类的实现。这意味着类ActualClassUnderTest
的测试可以开始中断,因为ClassUsedAsMock
的实现已更改。那给我散发出难闻的气味。用于测试ActualClassUnderTest
时最好只有突破ActualClassUnderTest
改变。
我意识到写断言是伪造是一种常见的做法,尤其是当您是 TDD 订户的嘲笑者类型时。我想我与马丁 · 福勒( Martin Fowler)在古典派阵营中坚定不移(请参阅马丁 · 福勒(Martin Fowler)的 “模仿不是存根” (Ockserove not's Stubs)),并且像 Osherove 一样,尽可能避免进行交互测试(只能通过断言来进行测试)。
为了有趣地阅读为什么您应该避免此处定义的模拟,请 google 搜索 “禽类模拟主义者古典主义者”。您会发现很多意见。