我知道我是如何使用这些术语,但是如果有接受伪造 , 嘲讽和磕碰的单元测试定义,我想知道? 你如何定义这些为你的测试? 描述一个你可能使用每情况。
下面是我如何使用它们:
假 :实现一个接口,但包含固定数据和没有逻辑的类。 简单地返回“好”或“坏”数据取决于执行。
模拟 :实现一个接口,允许动态设置的值返回/异常的具体方法抛出的能力,并能检查特定的方法被调用/不叫的能力的一类。
存根 :像模拟类,但它不提供验证方法被调用/不叫的能力。
嘲笑和短截线可被手产生或由模拟框架生成的。 假类由手产生的。 我用嘲笑主要是为了验证我的类和依赖类之间的相互作用。 我用的存根,一旦我已经验证的相互作用和我通过我的代码测试备用路径。 我用假类主要是为了抽象出的数据的依赖关系,或者当嘲笑/存根过于繁琐每次成立。
Answer 1:
你可以得到一些信息:
从Martin Fowler的有关模拟和存根
假对象实际上有工作的实现,但通常需要一定的快捷方式,这使得它们不适合生产
存根提供罐装答案在测试过程中进行的调用,通常不响应外界什么程序在测试什么。 存根还可以记录有关呼叫,比如记得它“已发送”的消息,或者也许只有多少消息是“已发送”邮件网关存根信息。
嘲笑是我们在这里谈论什么:对象预编程与形成他们预计接收呼叫的规范期望。
从xunitpattern :
假 :我们收购或建立一个非常轻量级的实现相同的功能,通过该SUT取决于并指示SUT用它代替真实的组件提供。
存根 :此实现配置为从与将行使未经测试的代码(见页X生产的漏洞)的SUT中的值(或异常)的SUT呼吁作出响应。 用于使用测试桩的一个关键指标是具有引起不能控制SUT的间接输入未经测试的代码
Mock对象实现相同的接口在其上SUT(待测系统)取决于一个对象。 我们可以使用模拟对象作为观测点,当我们需要做的动作验证,以避免未经检验的要求(见页X生产错误)所造成的无法观察被测系统调用方法的副作用。
亲身
我试图通过使用简化:模拟和存根。 我用模拟当它返回它被设置为被测试类值的对象。 我用存根来模拟一个接口或抽象类进行测试。 事实上,它并没有真正不管你叫它什么,他们是不是在生产中使用,并且作为公用事业类测试的所有类。
Answer 2:
存根 -提供预定义的回答方法调用的对象。
模拟 -在其上设定的预期目标。
假 -与有限的能力(用于测试的目的),例如伪造的Web服务的对象。
测试替身是存根,嘲笑和假货的总称。 但是非正式的,你会经常听到人们只需拨打他们嘲笑。
Answer 3:
我很惊讶,这个问题已经存在了这么久,没有人尚未提供基于一个答案罗伊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
改变。
我认识到,写作声称对假货是一种常见的做法,尤其是当你是一个mockist型TDD订户。 我想我与Martin Fowler的是牢牢地掌握在古典主义阵营(见Martin Fowler的“嘲笑不是存根” )和类似Osherove避免相互影响测试(只能通过声明对假货进行)尽可能。
为了好玩阅读为什么你应该避免此处定义嘲笑,谷歌的“福勒mockist古典主义”。 你会发现意见过多。
Answer 4:
为了说明存根和嘲笑的使用,我想也包括基于罗伊Osherove的“一个例子单元测试的艺术 ”。
试想一下,我们有具有打印日志的唯一功能的纪录分析工具的应用。 它不仅需要跟一个Web服务,但如果Web服务引发错误,纪录分析工具有错误日志,以不同的外部依赖,通过电子邮件将其发送到Web服务管理员。
下面是我们想里面纪录分析工具测试逻辑:
if(fileName.Length<8)
{
try
{
service.LogError("Filename too short:" + fileName);
}
catch (Exception e)
{
email.SendEmail("a","subject",e.Message);
}
}
你怎么测试纪录分析工具正确调用电子邮件服务时,Web服务将引发异常? 下面是我们面临的问题:
我们可以处理利用存根Web服务的前两个问题。 为了解决第三个问题,我们可以使用一个模拟对象的电子邮件服务 。
一个假是可以用来描述任何存根或一个mock.In我们的测试中,我们将有两个假货的总称。 一会是电子邮件服务模拟,我们将使用它来验证正确的参数发送到电子邮件服务。 另将是我们用来模拟从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);
}
}
Answer 5:
这使得测试表现的问题。 我设置一个模拟的期望,如果我想测试来描述两个对象之间的关系。 我存根,如果我设置了一个支撑物,让我在测试的有趣行为的返回值。
Answer 6:
如果您熟悉安排-ACT-断言,则说明可能对您有用存根和模拟之间的差别的一种方法,是存根属于安排部分,因为它们是安排输入状态,并嘲笑属于断言部分,因为它们是断言结果反对。
假人没有做任何事情。 他们只是为了填补参数列表,这样你就不会得到未定义或为空的错误。 他们也存在,以满足严格的类型语言的类型检查,这样就可以允许编译和运行。
Answer 7:
你就可以断言的东西,被称为模拟对象和其他一切只是帮助了试运行,是一个存根 。
Answer 8:
作为由得票最多的答复中提到,Martin Fowler的讨论这些差异在模拟的功能没有存根 ,尤其副标题嘲弄和存根之间的差异 ,所以一定要读到了那篇文章。
而不是着眼于这些东西是如何的不同,我认为这是更具启发专注于为什么这些是不同的概念。 每个存在不同的目的。
假货
一个假的行为方式与“自然”的实现,而不是“真实的”。 这些都是模糊概念,因此不同的人有什么让事情假货不同的理解。
一个假的一个例子是一个内存数据库(例如使用SQLite与:memory:
存储)。 你绝不会用这个生产(由于数据不保留),但它是完全足够的数据库,在测试环境中使用。 它也比一个“真正”的数据库更轻便。
再举一个例子,也许你使用某种类型的对象存储(如亚马逊S3)生产的,但在一个测试,你可以简单地保存在磁盘上的对象文件; 那么你的“保存到磁盘”的实施将是一个假的。 (或者你可以甚至伪造使用一个内存中的文件系统,而不是“保存到磁盘”操作。)
作为第三个例子,假设一个目的,提供了一个高速缓存API; 实现了正确的接口,但是,简单地在所有不进行缓存,但总是一个对象返回高速缓存未命中将是一种假的。
一个假的目的是不影响系统的行为测试 ,而是(通过去除不必要的或重量级依赖关系),以简化测试的执行 。
存根
存根是行为“不自然”的实现。 它被预先配置(通常由测试设置),以与特定的输出的特定输入回应。
存根的目的是为了获得被测系统进入特定的状态。 例如,如果你正在编写一些代码,与REST API交互的测试,你可以存根出与总是返回罐头回应,或者回应与特定错误的API请求的API的REST API。 这样,你可以写测试,作出关于系统如何应对这些国家的断言; 例如,测试响应用户获取如果API返回404错误。
存根通常被实现到只有你告诉它响应的精确交互响应。 但关键的功能,使东西存根它的目的是:存根是所有关于设置你的测试用例。
嘲弄
有模拟的是类似stub,但核查 。添加一个模拟的目的是为了让你的测试系统如何与依赖互动的断言 。
例如,如果你正在写一个系统测试文件上传到一个网站,你可以建立一个接受文件模拟效果和您可以使用断言,上传的文件是正确的。 或者,在一个小范围内,这是常见的使用对象的模拟来验证被测系统调用的嘲笑对象的具体方法。
嘲笑是绑互动测试 ,这是一个特定的测试方法。 谁愿意来测试系统的状态 ,而不是系统的相互作用的人会用嘲弄谨慎,如果在所有。
测试双打
假货,存根和嘲笑都属于测试双打的范畴。 测试双是在测试中,而不是别的什么用任何物体或系统。 大多数的自动化软件测试包括使用一些这样或那样的测试双打的。 一些其他类型的测试双打的包括虚值 , 间谍和I / O 黑洞 。
Answer 9:
存根和假是,他们可以基于输入参数改变它们的响应对象。 它们之间的主要区别是假的更接近真实世界的实现比存根。 存根包含基本上与预期的请求硬编码的响应。 让我们来看一个例子:
public class MyUnitTest {
@Test
public void testConcatenate() {
StubDependency stubDependency = new StubDependency();
int result = stubDependency.toNumber("one", "two");
assertEquals("onetwo", result);
}
}
public class StubDependency() {
public int toNumber(string param) {
if (param == “one”) {
return 1;
}
if (param == “two”) {
return 2;
}
}
}
有模拟的是从假货和存根升压。 嘲弄提供相同的功能存根,但更复杂。 他们可以在一定要叫上他们的API什么顺序方法决定为它们定义的规则。 大多数嘲弄可以跟踪多少次的方法被调用,并能反应基于这些信息。 嘲笑大致知道每个呼叫的情况下,可以在不同的情况做出不同的反应。 正因为如此,嘲笑要求他们嘲讽类的一些知识。 存根通常不能跟踪方法调用多少次或以什么顺序的方法的顺序被调用。 有模拟的样子:
public class MockADependency {
private int ShouldCallTwice;
private boolean ShouldCallAtEnd;
private boolean ShouldCallFirst;
public int StringToInteger(String s) {
if (s == "abc") {
return 1;
}
if (s == "xyz") {
return 2;
}
return 0;
}
public void ShouldCallFirst() {
if ((ShouldCallTwice > 0) || ShouldCallAtEnd)
throw new AssertionException("ShouldCallFirst not first thod called");
ShouldCallFirst = true;
}
public int ShouldCallTwice(string s) {
if (!ShouldCallFirst)
throw new AssertionException("ShouldCallTwice called before ShouldCallFirst");
if (ShouldCallAtEnd)
throw new AssertionException("ShouldCallTwice called after ShouldCallAtEnd");
if (ShouldCallTwice >= 2)
throw new AssertionException("ShouldCallTwice called more than twice");
ShouldCallTwice++;
return StringToInteger(s);
}
public void ShouldCallAtEnd() {
if (!ShouldCallFirst)
throw new AssertionException("ShouldCallAtEnd called before ShouldCallFirst");
if (ShouldCallTwice != 2) throw new AssertionException("ShouldCallTwice not called twice");
ShouldCallAtEnd = true;
}
}
Answer 10:
我学到了很多后续的资源,由罗伯特C.马丁(Bob大叔)一个很好的解释:
小莫克在干净的代码博客
它解释之间的微妙差异
它还提到马丁·福勒,并介绍了一点软件测试的历史。
绝不,我打算回答这个链接这个问题为真实的答案。 然而,它帮助我理解嘲讽和间谍更好的概念,所以我回答这个希望它会帮助更多的人。
文章来源: What's the difference between faking, mocking, and stubbing?