我知道我如何使用这些术语,可是我想知道是否存在接受伪造 , 模拟和存根的单元测试定义? 您如何为测试定义这些? 描述您可能会使用每种状况的状况。 html
这是个人用法: 编程
Fake :实现接口但包含固定数据且没有逻辑的类。 只需根据实现返回“好”或“坏”数据。 框架
Mock :一个实现接口并容许动态设置要返回的值/从特定方法抛出异常的类,并提供检查是否已调用/未调用特定方法的能力。 单元测试
存根(Stub) :相似于模拟类,不一样之处在于它不提供验证方法是否已被调用的能力。 学习
模拟和存根能够手动生成,也能够由模拟框架生成。 伪类是手工生成的。 我主要使用模拟来验证个人类和依赖类之间的交互。 一旦验证了交互做用并测试了代码中的替代路径,便会使用存根。 我主要使用伪造的类来抽象出数据依赖性,或者当模拟/存根过于繁琐而没法每次设置时。 测试
让我感到惊讶的是,这个问题已经存在了很长时间,并且尚未人根据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同样,尽量避免进行交互测试(只能经过断言来进行测试)。
有趣的阅读,为何你应该避免在这里定义的模拟,谷歌为“福勒模拟古典主义者”。 您会发现不少意见。
您能够得到一些信息:
伪对象实际上具备有效的实现,可是一般采起一些捷径,这使它们不适合生产
存根提供对测试过程当中进行的呼叫的固定答复,一般一般根本不响应为测试编程的内容。 存根还能够记录有关呼叫的信息,例如,电子邮件网关存根能够记住“已发送”的消息,或者仅记住“已发送”的消息数量。
嘲讽是咱们在这里谈论的:带有指望的预编程对象,这些对象造成了指望接收的呼叫的规范。
伪造的 :咱们获取或构建一种很是轻量级的功能,实现与SUT依赖的组件所提供的功能相同的功能,并指示SUT使用它而不是真实的。
存根(Stub) :此实现被配置为使用将执行SUT中未经测试的代码(请参阅第X页的生产错误)的值(或异常)响应来自SUT的调用。 使用测试存根的一个关键指示是因为没法控制SUT的间接输入而致使未测试代码
模拟对象 ,实现与SUT(被测系统)所依赖的对象相同的接口。 当咱们须要进行行为验证时,能够将模拟对象用做观察点,以免因没法观察调用方法对SUT的反作用而致使未经测试的需求(请参阅第X页的生产错误)。
我尝试经过使用简化:模拟和存根。 当它是一个返回设置为测试类的值的对象时,我会使用Mock。 我使用Stub模仿要测试的Interface或Abstract类。 实际上,您所说的并不重要,它们都是生产中不使用的类,而且用做测试的实用程序类。
若是您熟悉Arrange-Act-Assert,那么解释存根和模拟之间的差别可能对您有用的一种方法是,存根属于安排部分,由于它们是用于排列输入状态的,而模拟属于断言部分,由于它们用于断言结果。
假人什么也没作。 它们仅用于填充参数列表,所以不会出现未定义或空错误。 它们也能够知足严格类型化语言中的类型检查器的要求,所以能够容许您编译和运行它们。
为了说明存根和模拟的用法,我还要列举一个基于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); } }
您在其中声明的东西称为模拟对象,而其余仅有助于测试运行的东西都是存根 。