2009-11-11 28 views
17

我曾经写过NUnit测试的代码。但是,我从来没有用过嘲笑框架。他们是什么?我理解依赖注入以及它如何帮助提高可测试性。我的意思是所有的依赖可以在单元测试时被模拟。但是,那么为什么我们需要嘲笑框架呢?我们不能简单地创建模拟对象并提供依赖关系。我在这里错过了什么吗? 谢谢。为什么我们需要嘲笑框架?

回答

12
  • 它使嘲弄更容易
  • 他们通常 让你表达可测试 断言参考对象之间的互动 。

这里有一个例子:

var extension = MockRepository 
    .GenerateMock<IContextExtension<StandardContext>>(); 
    var ctx = new StandardContext(); 
    ctx.AddExtension(extension); 
    extension.AssertWasCalled(
    e=>e.Attach(null), 
    o=>o.Constraints(Is.Equal(ctx))); 

你可以看到,我明确地测试该IContextExtension的连接方法被调用,并且据说上下文对象的输入参数。如果没有发生,这会使我的测试失败。

2

使用嘲讽库的唯一原因是它使嘲笑更容易。

当然,你可以在没有图书馆的情况下做到这一切,如果它很简单,那很好,但只要它们开始变得复杂,图书馆就容易得多。

想到这个排序算法,确定任何人都可以写一个,但为什么?如果代码已经存在并且很容易调用...为什么不使用它?

+2

有些嘲讽框架不仅仅是手工完成,嘲笑静态。 – 2009-11-14 19:03:21

11

您可以手动创建模拟对象,并在使用依赖注入框架进行测试时使用它们,但让模拟框架为您生成模拟对象可节省时间。

一如既往,如果使用该框架增加了太多的复杂性,那么请不要使用它。

1

你当然可以手动嘲弄你的依赖关系,但是对于一个框架来说,它需要花费很多繁琐的工作。此外,通常可用的断言使其值得学习。

3

使用一个模拟框架可以是一个更轻量级和简单的解决方案来提供模拟,而不是为每个想要模拟的对象创建一个模拟对象。

例如,模拟框架对验证呼叫是否已完成(甚至是进行了多少次呼叫)等事情特别有用。制作你自己的模拟对象来检查这样的行为(嘲笑行为本身就是一个主题)是单调乏味的,而且还有一个地方可以引入一个bug。

查看Rhino Mocks了解嘲笑框架的强大程度。

7

有时,在使用第三方库或者甚至使用.NET框架的某些方面时,在某些情况下编写测试非常困难 - 例如,HttpContext或Sharepoint对象。为这些创建模拟对象可能会变得非常麻烦,所以嘲笑框架会关注基础知识,以便我们花时间专注于使我们的应用程序具有独特性的原因。

3

模拟对象代替您的代码需要访问才能运行的任何大型/复杂/外部对象。

他们是有几个原因是有益的:

  • 你的测试是为了快速,轻松地运行。如果你的代码依赖于数据库连接,那么你需要运行一个完全配置和填充的数据库才能运行你的测试。这可能会让人讨厌,所以你创建一个替换 - 一个“模拟” - 数据库连接对象,只是模拟数据库。

  • 您可以准确控制来自Mock对象的输出,因此可以将它们用作可控数据源来进行测试。

  • 您可以在之前创建模拟,然后创建实际对象以优化其界面。这在测试驱动开发中很有用。

0

好嘲讽框架使我的生活变得更轻松,少繁琐,所以我可以把时间花在实际编写代码。如果你有MyComplex.class然后有一个模拟框架的价值就不言而喻了更换List.class就拿的Mockito(在Java世界中)

//mock creation 
List mockedList = mock(List.class); 

//using mock object 
mockedList.add("one"); 
mockedList.clear(); 

//verification 
verify(mockedList).add("one"); 
verify(mockedList).clear(); 

//stubbing using built-in anyInt() argument matcher 
when(mockedList.get(anyInt())).thenReturn("element"); 

//stubbing using hamcrest (let's say isValid() returns your own hamcrest matcher): 
when(mockedList.contains(argThat(isValid()))).thenReturn("element"); 

//following prints "element" 
System.out.println(mockedList.get(999)); 

虽然这是一个人为的例子。你可以自己写或不写,但为什么你想要走这条路。

0

我首先想到了为什么我需要一个模拟框架,当我比较手动编写单元测试集(每个测试需要稍微不同的行为,因此我为每个测试创建基类假类型的子类)使用像RhinoMocks或Moq这样的工具来完成相同的工作。

简单地说,使用框架生成所需的所有假对象比手动编写(并调试)我自己的假货要快得多。

1

模拟框架允许您从代码的依赖关系中隔离要测试的代码单元。它们还允许您在测试环境中模拟代码依赖项的各种行为,否则可能难以设置或复制。

例如,如果我有一个包含我想测试的业务规则和逻辑的类A,但这个类A依赖于数据访问类,其他业务类,甚至是u/i类等,则这些其他类类可以被嘲笑以某种方式执行(或者在松散的模拟行为的情况下根本没有任何方式)来基于这些其他类在生产环境中可以想象的行为的每种可想象的方式来测试A类中的逻辑。

举一个更深的例子,假设你的类A调用方法对数据访问类如

public bool IsOrderOnHold(int orderNumber) {} 

然后将数据访问类的模拟可以设置每一个时间或返回true每次都返回false,以测试你的A类如何回应这种情况。

+0

好说。模拟允许行为被重写。 – j2emanue 2015-04-22 15:54:13

1

我声称你没有。编写测试双打并不是一件很麻烦的事情,在10次的9次之内。大部分时间它几乎完全是通过请求resharper为你实现一个接口自动完成的,然后你只需添加这个double所需的小细节(因为你没有做一堆逻辑和创造这些错综复杂的超级测试双打,对吧?)

“但是为什么我想让我的测试项目臃肿一堆测试双打?”你可能会问。那么你不应该。 DRY原则也适用于测试。创建可重复使用并具有描述性名称的良好测试双打。这使您的测试更具可读性。

有一件事更难做的是过度使用测试双打。我倾向于同意Roy Osherove和Bob叔叔,你真的不想用一些特殊的配置创建一个模拟对象。这本身就是一种设计气味。使用一个框架,在几乎每个测试中都可以使用复杂的逻辑来使用测试双打,并且最终你会发现你并没有真正测试过你的生产代码,但你只是测试了神奇的弗兰肯斯坦的一系列含有模拟嘲笑更多的嘲笑。如果你写自己的双打,你永远不会“意外地”做到这一点。

当然,有人会指出有时候你“有”使用框架,而不是这样做会是愚蠢的。当然,有这种情况。但你可能没有这种情况。大多数人不会,只有一小部分代码或代码本身非常糟糕。

我推荐任何人(特别是初学者)远离框架,学习如何在没有框架的情况下学习,然后当他们觉得他们真的必须使用他们认为最合适的框架时,但到那时它将成为一个明智的解决方案,他们将不太可能滥用框架来创建错误的代码。