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

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

这是个人用法: 编程

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

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

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

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


#1楼

让我感到惊讶的是,这个问题已经存在了很长时间,并且尚未人根据Roy Osherove的“单元测试的艺术”给出答案。 spa

在“ 3.1介绍存根”中,将存根定义为: 设计

存根是系统中现有依赖项(或协做者)的可控替代。 经过使用存根,您能够测试代码而无需直接处理依赖项。 日志

并将存根和模拟之间的区别定义为: code

关于模拟与存根之间要记住的主要事情是,模拟就像存根同样,可是您针对模拟对象断言,而您不针对存根进行断言。

虚假只是存根和模拟的名称。 例如,当您不关心存根和模拟之间的区别时。

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的实现ClassUsedAsMock更改。 那给我散发出难闻的气味。 用于测试ActualClassUnderTest时最好只有突破ActualClassUnderTest改变。

我意识到写断言是对伪造的一种惯例,尤为是当您是TDD订户的嘲笑者类型时。 我想我与马丁·福勒(Martin Fowler)在古典主义阵营中坚决不移(请参阅马丁·福勒Martin Fowler)的“模仿不是存根” (Ockserove not not Stubs )),而且像Osherove同样,尽量避免进行交互测试(只能经过断言来进行测试)。

有趣的阅​​读,为何你应该避免在这里定义的模拟,谷歌为“福勒模拟古典主义者”。 您会发现不少意见。


#2楼

您能够得到一些信息:

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

对象实际上具备有效的实现,可是一般采起一些捷径,这使它们不适合生产

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

嘲讽是咱们在这里谈论的:带有指望的预编程对象,这些对象造成了指望接收的呼叫的规范。

xunitpattern

伪造的 :咱们获取或构建一种很是轻量级的功能,实现与SUT依赖的组件所提供的功能相同的功能,并指示SUT使用它而不是真实的。

存根(Stub) :此实现被配置为使用将执行SUT中未经测试的代码(请参阅第X页的生产错误)的值(或异常)响应来自SUT的调用。 使用测试存根的一个关键指示是因为没法控制SUT的间接输入而致使未测试代码

模拟对象 ,实现与SUT(被测系统)所依赖的对象相同的接口。 当咱们须要进行行为验证时,能够将模拟对象用做观察点,以免因没法观察调用方法对SUT的反作用而致使未经测试的需求(请参阅第X页的生产错误)。

亲身

我尝试经过使用简化:模拟和存根。 当它是一个返回设置为测试类的值的对象时,我会使用Mock。 我使用Stub模仿要测试的Interface或Abstract类。 实际上,您所说的并不重要,它们都是生产中不使用的类,而且用做测试的实用程序类。


#3楼

若是您熟悉Arrange-Act-Assert,那么解释存根和模拟之间的差别可能对您有用的一种方法是,存根属于安排部分,由于它们是用于排列输入状态的,而模拟属于断言部分,由于它们用于断言结果。

假人什么也没作。 它们仅用于填充参数列表,所以不会出现未定义或空错误。 它们也能够知足严格类型化语言中的类型检查器的要求,所以能够容许您编译和运行它们。


#4楼

为了说明存根和模拟的用法,我还要列举一个基于Roy Osherove的“ 单元测试的艺术 ”的示例。

想象一下,咱们有一个LogAnalyzer应用程序,它具备打印日志的惟一功能。 它不只须要与Web服务对话,并且若是Web服务引起错误,则LogAnalyzer必须将错误记录到其余外部依赖项中,并经过电子邮件将其发送给Web服务管理员。

这是咱们要在LogAnalyzer中测试的逻辑:

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

当Web服务引起异常时,如何测试LogAnalyzer正确调用电子邮件服务? 这是咱们面临的问题:

  • 咱们如何替换Web服务?

  • 咱们如何模拟来自Web服务的异常,以便咱们能够测试对电子邮件服务的呼叫?

  • 咱们如何知道电子邮件服务被正确调用或彻底被调用?

经过使用Web服务的存根,咱们能够处理前两个问题。 为了解决第三个问题,咱们能够将模拟对象用于电子邮件服务

假货是一个通用术语,能够用来描述存根或模拟。在咱们的测试中,咱们将有两个假货。 一种将是电子邮件服务模拟,咱们将使用它来验证是否已将正确的参数发送到电子邮件服务。 另外一个将是一个存根,咱们将使用它来模拟从Web服务引起的异常。 这是一个存根,由于咱们不会使用伪造的Web服务来验证测试结果,只是为了确保测试正确运行。 电子邮件服务是一个模拟,由于咱们会断言它已被正确调用。

[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);
 }
}

#5楼

您在其中声明的东西称为模拟对象,而其余仅有助于测试运行的东西都是存根

相关文章
相关标签/搜索