我使用过编写过NUnit测试的代码。但是,我从未使用过模拟框架。这些是什么?我理解依赖注入以及它如何帮助提高可测试性。我的意思是所有依赖项都可以在单元测试时进行模拟。但是,为什么我们需要模拟框架呢?我们不能简单地创建模拟对象并提供依赖关系。我在这里错过了什么吗? 感谢。
答案 0 :(得分: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的Attach方法被调用,输入参数是上下文对象。如果没有发生,它会使我的测试失败。
答案 1 :(得分:11)
您可以手动创建模拟对象并在测试期间使用依赖注入框架...但让模拟框架为您生成模拟对象可以节省时间。
与往常一样,如果使用框架会增加太多复杂性而无法使用它。
答案 2 :(得分:7)
有时在使用第三方库,甚至使用.NET框架的某些方面时,在某些情况下编写测试非常困难 - 例如,HttpContext或Sharepoint对象。为那些创建模拟对象会变得非常麻烦,因此模拟框架会处理基础知识,因此我们可以将时间花在专注于使应用程序独特的内容上。
答案 3 :(得分:3)
使用模拟框架可以提供模拟的轻量级和简单的解决方案,而不是为要模拟的每个对象实际创建模拟对象。
例如,模拟框架对于执行诸如验证调用之类的操作(甚至调用的次数)非常有用。制作自己的模拟对象来检查这样的行为(虽然模拟行为本身就是一个主题)是单调乏味的,而且是另一个引入错误的地方。
查看Rhino Mocks以获取模拟框架功能强大的示例。
答案 4 :(得分:3)
Mock对象取代了代码需要访问的任何大型/复杂/外部对象才能运行。
由于以下几个原因,它们是有益的:
您的测试旨在快速轻松地运行。如果您的代码依赖于数据库连接,那么您需要运行完全配置和填充的数据库才能运行测试。这可能会很烦人,所以你创建了一个替换 - 一个“模拟” - 只是模拟数据库的数据库连接对象。
您可以精确控制Mock对象的输出,因此可以将它们用作测试的可控数据源。
您可以在创建真实对象之前创建模拟以优化其界面。这在测试驱动开发中很有用。
答案 5 :(得分:2)
使用模拟库的唯一原因是它使模拟变得更容易。
当然,你可以在没有库的情况下完成所有工作,如果它很简单就可以了,但是一旦它们开始变得复杂,库就会容易得多。
在排序算法方面考虑这一点,确定任何人都可以写一个,但为什么?如果代码已经存在并且很容易调用...为什么不使用它呢?
答案 6 :(得分:1)
你当然可以手动模拟你的依赖项,但是使用框架需要花费大量繁琐的工作。通常可用的断言也值得学习。
答案 7 :(得分:1)
模拟框架允许您从该代码的依赖项中隔离您希望测试的代码单元。它们还允许您在测试环境中模拟代码依赖关系的各种行为,否则可能难以设置或重现。
例如,如果我有一个包含我希望测试的业务规则和逻辑的A类,但是这个A类依赖于数据访问类,其他业务类,甚至是u / i类等,这些其他类可以被模拟以某种方式执行(或者在松散的模拟行为的情况下完全不执行),以基于这些其他类可以想象的方式在生产环境中行为来测试类A中的逻辑。 / p>
为了给出更深层的例子,假设你的类A在数据访问类上调用一个方法,例如
public bool IsOrderOnHold(int orderNumber) {}
然后可以将该数据访问类的模拟设置为每次返回true或每次都返回false,以测试A类如何响应这种情况。
答案 8 :(得分:1)
我声称你没有。编写测试双打并不是10次中的9次大家务。大部分时间它只是通过让resharper为你实现一个接口而几乎完全自动完成,然后你只需添加这个双重所需的次要细节(因为你没有做一堆逻辑并创建这些错综复杂的超级测试双打,对吧?对吗?)
“但我为什么要让我的测试项目充满一堆测试双打”你可能会问。那你不应该。 DRY原则也适用于测试。创建可重复使用且具有描述性名称的GOOD测试双精度数。这使您的测试更具可读性。
更难做的一件事是过度使用测试双打。我倾向于同意Roy Osherove和Uncle Bob,你真的不想创建一个经常使用一些特殊配置的模拟对象。这本身就是一种设计气味。使用一个框架就可以很容易地在几乎每个测试中使用复杂逻辑的测试双精度,最后你发现你还没有真正测试过你的生产代码,你只是测试了那个令人敬畏的frankenstein的monsteresque混乱的模拟包含嘲笑包含更多的嘲笑。如果你写自己的双打,你永远不会“意外地”这样做。
当然,有人会指出,有时候你“有”使用框架,不这样做会非常愚蠢。当然,有这样的情况。但你可能没有那种情况。大多数人没有,只有一小部分代码,或代码本身非常糟糕。
我建议任何人(特别是初学者)远离框架并学习如何在没有它们的情况下顺利过关,然后当他们觉得他们真的需要时他们可以使用他们认为最合适的框架,但到那时它将是一个明智的决定,他们将不太可能滥用框架来创建错误的代码。
答案 9 :(得分:0)
好的模拟框架让我的生活更轻松,更乏味,所以我可以花时间实际编写代码。以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));
虽然如果用List.class
替换MyComplex.class
,这是一个人为的例子,那么拥有模拟框架的价值就变得明显了。你可以写自己的,也可以不做,但你为什么要走那条路。
答案 10 :(得分:0)
我首先想知道为什么我需要一个模拟框架,当我比较一组单元测试时手动编写测试双精度(每个测试需要稍微不同的行为,所以我为每个测试创建一个基本假类型的子类)使用像RhinoMocks或Moq这样的东西做同样的工作。
简单地说,使用框架生成我需要的所有虚假对象要快得多,而不是手工编写(和调试)我自己的假货。