伪装,嘲弄和剔除有什么区别?

我知道我是如何使用这些术语的,但是我想知道是否有可接受的用于unit testing的伪造嘲弄剔除的定义? 你如何定义这些testing? 描述你可能使用的情况。

以下是我如何使用它们:

Fake :实现接口但包含固定数据和无逻辑的类。 根据实施情况简单地返回“好”或“坏”数据。

模拟(Mock) :一个实现了接口的类,允许dynamic设置值的返回/exception,从特定方法抛出,并提供检查特定方法是否被调用/未调用的能力。

存根 :就像一个模拟类,除了它没有提供validation方法被调用/不被调用的能力。

嘲笑和存根(stub)可以由手工生成或由嘲笑框架生成。 假类是手工生成的。 我主要使用mock来validation我的类和相关类之间的交互。 一旦我validation了交互,并使用我的代码testing替代path,我使用了存根(stub)。 我主要使用假类来抽象出数据的依赖关系,或者当模拟/存根(stub)过于繁琐而无法每次设置时。

你可以得到一些信息:

从马丁福勒关于模拟和存根

虚假的对象实际上有工作的实现,但通常采取一些捷径,使他们不适合生产

存根提供了在testing过程中进行调用的jar装答案,通常对testing以外的任何事情都没有响应。 存根还可以logging关于呼叫的信息,例如logging其“发送”消息的电子邮件网关存根,或者可能仅logging“发送”的消息的数量。

嘲笑是我们在这里谈论的东西:预先编程有预期的对象,这些预期构成了预期接收的呼叫的规格。

从xunitpattern :

Fake :我们获取或构build一个与SUT依赖的组件提供的相同function的轻量级实现,并指示SUT使用它而不是真实的。

存根(stub) :这个实现被configuration为响应来自SUT的调用,其中的值(或例外)将在SUT中执行未经testing的代码(参见第X页的生产错误)。 使用testing桩的一个关键指标是由于无法控制SUT的间接input而导致的未testing代码

Mock对象 ,实现与SUT(被测系统)所依赖的对象相同的接口。 当我们需要进行行为validation以避免由于无法观察SUT上调用方法的副作用而导致未经testing的要求(参见第X页的生产错误)时,我们可以使用模拟对象作为观察点。

亲自

我尝试通过使用:模拟和存根(stub)来简化。 当它是一个对象返回一个设置为被testing类的值时,我使用Mock。 我使用存根来模仿要testing的接口或抽象类。 实际上,你所说的并不重要,它们都是不在生产中使用的类,并且被用作testing的工具类。

存根(Stub) – 一个为方法调用提供预定义答案的对象。

模拟 – 您设定期望的对象。

– 一个function有限的对象(用于testing),例如假的Web服务。

testing双是存根,嘲笑和假货的总称。 但非正式的,你会经常听到人们简单地称他们为嘲笑。

令我感到惊讶的是,这个问题已经存在了很长时间,还没有人提供罗伊·奥谢罗夫(Roy Osherove)的“unit testing的艺术”的答案。

在“3.1引言存根”中定义了一个存根:

存根是系统中现有依赖关系(或协作者)的可控替代品。 通过使用存根,您可以testing您的代码而不直接处理依赖关系。

并定义存根和嘲讽之间的区别为:

关于嘲笑与存根的主要问题是嘲笑就像存根,但是你对这个嘲讽的对象断言,而你没有断言存根。

假只是用于存根和嘲讽的名字。 例如,当你不关心存根和嘲讽之间的区别时。

Osherove区分存根和嘲讽的方式意味着用作testing的任何类别都可以是存根或模拟。 它是一个具体的testing完全取决于你如何在你的testing书写检查。

  • 当你的testing在被testing的类中检查值,或者实际上除了假的地方以外的任何地方,假的被用作存根。 它只是提供被testing类的值,可以直接通过调用返回的值,也可以通过调用副作用(某些状态)间接调用。
  • 当你的testing检查假冒的价值,它被用来作为一个模拟。

类FakeX用作存根的testing示例:

const pleaseReturn5 = 5; var fake = new FakeX(pleaseReturn5); var cut = new ClassUnderTest(fake); cut.SquareIt; Assert.AreEqual(25, cut.SomeProperty); 

fake实例被用作存根,因为Assert根本不使用fake

将testing类别X用作模拟的testing示例:

 const pleaseReturn5 = 5; var fake = new FakeX(pleaseReturn5); var cut = new ClassUnderTest(fake); cut.SquareIt; Assert.AreEqual(25, fake.SomeProperty); 

在这种情况下, Assert检查一个fake的价值,使这个假的模拟。

现在,这些例子当然是非常有意思的,但是我看到这个区别有很大的价值。 它让你意识到你如何testing你的东西以及testing的依赖关系。

我同意Osherove的那个

从纯粹的可维护性的angular度来看,在我的testing中使用mock会比不使用它们造成更多的麻烦。 这是我的经验,但我总是在学习新的东西。

主张反对假冒是你真正想要避免的,因为它使得你的testing高度依赖于不是被testing类的实现。 这意味着类ActualClassUnderTest的testing可以开始破坏,因为ClassUsedAsMock的实现ClassUsedAsMock更改。 这给我一个恶臭。 ActualClassUnderTesttesting应该最好只当ActualClassUnderTest改变时才会中断。

我意识到写出反对假冒的做法是很常见的做法,特别是当你是一个模仿者的TDD用户时。 我想我是和马丁·福勒(Martin Fowler)在古典主义阵营(参见Martin Fowler的“ Mocks is not Stubs” )中坚定不移的,并且像Osherove尽可能地避免了交互testing(这只能通过对抗假冒来完成)。

为了有趣的阅读,为什么你应该避免这里定义的嘲笑,谷歌“fowler嘲弄古典主义”。 你会发现过多的意见。

这是一个使testingexpression的问题。 如果我想要testing来描述两个对象之间的关系,我可以在Mock上设置期望值。 如果我设置了一个支持对象来让我在testing中感兴趣的行为,我存根返回值。

为了说明存根和嘲讽的用法,我还想列举一个基于Roy Osherove的“ unit testing的艺术 ”的例子。

想象一下,我们有一个LogAnalyzer应用程序,它具有打印日志的唯一function。 它不仅需要与Web服务对话,而且如果Web服务引发错误,LogAnalyzer必须将错误logging到不同的外部依赖项,并通过电子邮件将其发送给Web服务pipe理员。

以下是我们想要在LogAnalyzer中testing的逻辑:

 if(fileName.Length<8) { try { service.LogError("Filename too short:" + fileName); } catch (Exception e) { email.SendEmail("a","subject",e.Message); } } 

当Web服务引发exception时,如何testingLogAnalyzer正确调用电子邮件服务? 以下是我们所面临的问题:

  • 我们如何更换networking服务?

  • 我们如何模拟Web服务中的exception,以便testing对电子邮件服务的调用?

  • 我们如何知道电子邮件服务是否正确调用?

我们可以通过使用Web服务的存根来处理前两个问题。 为了解决第三个问题,我们可以为电子邮件服务使用模拟对象

假货是一个通用的术语,可以用来描述存根或模拟。在我们的testing中,我们将有两个假货。 一个将是电子邮件服务模拟,我们将用它来validation正确的参数发送到电子邮件服务。 另一个将是我们将用来模拟从Web服务抛出的exception的存根。 这是一个存根,因为我们不会使用Web服务假来validationtesting结果,只是为了确保testing运行正确。 电子邮件服务是一个模拟,因为我们会断言它是正确的。

 [TestFixture] public class LogAnalyzer2Tests { [Test] public void Analyze_WebServiceThrows_SendsEmail() { StubService stubService = new StubService(); stubService.ToThrow= new Exception("fake exception"); MockEmailService mockEmail = new MockEmailService(); LogAnalyzer2 log = new LogAnalyzer2(); log.Service = stubService log.Email=mockEmail; string tooShortFileName="abc.ext"; log.Analyze(tooShortFileName); Assert.AreEqual("a",mockEmail.To); //MOCKING USED Assert.AreEqual("fake exception",mockEmail.Body); //MOCKING USED Assert.AreEqual("subject",mockEmail.Subject); } } 

我熟悉Arrange-Act-Assert,那么解释stub和mock之间可能对你有用的区别的一种方式就是stub属于排列部分,就像排列input状态一样,而mock属于assert部分,因为它们是用来声明结果的。

傻瓜不做任何事情。 它们只是用来填充参数列表,所以你不会得到未定义的或空的错误。 它们也存在以严格types的语言来满足types检查器,以便允许你编译和运行。