协慌网

登录 贡献 社区

伪造,嘲笑和存根之间有什么区别?

我知道如何使用这些术语,但是我想知道是否存在接受伪造模拟存根的单元测试定义?您如何为测试定义这些?描述您可能会使用每种情况的情况。

这是我的使用方式:

Fake :实现接口但包含固定数据且没有逻辑的类。只需根据实现返回 “好” 或 “坏” 数据。

Mock :一个实现接口的类,该类允许动态设置要返回的值 / 从特定方法抛出的异常,并提供检查是否已调用 / 未调用特定方法的能力。

存根(Stub) :类似于模拟类,不同之处在于它不提供验证方法是否已被调用的能力。

模拟和存根可以手动生成,也可以由模拟框架生成。伪类是手工生成的。我主要使用模拟来验证我的类和依赖类之间的交互。一旦验证了交互作用并正在测试代码中的替代路径,便会使用存根。我主要使用伪造的类来抽象出数据依赖关系,或者当模拟 / 存根过于繁琐而无法每次设置时。

答案

您可以获得一些信息:

马丁 · 福勒(Martin Fowler)着《模拟与存根》

对象实际上具有有效的实现,但是通常采取一些捷径,这使它们不适合生产

存根提供对测试期间进行的呼叫的固定答复,通常通常根本不响应为测试编程的内容。存根还可以记录有关呼叫的信息,例如,电子邮件网关存根可以记住 “已发送” 的消息,或者仅记住 “已发送” 的消息数量。

嘲笑是我们在这里谈论的话题:对象被预编程并带有期望,形成了期望接收的呼叫的规范。

xunitpattern

伪造的:我们获取或构建一个非常轻量级的功能,实现与 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 搜索 “禽类模拟主义者古典主义者”。您会发现很多意见。