为什么要使用模拟框架?

时间:2011-07-04 16:56:10

标签: c# .net mocking moq rhino-mocks

我们目前正在使用Autofac作为IoC容器来关注DI模型。

我们最近开始研究像MOQ和Rhino Mocks这样的模拟框架。但是,我们似乎无法证明它们仅用于为每个接口创建Mock实现类。

为什么这样做:

var mock = new Mock<IFoo>();
mock.Setup(foo => foo.DoSomething("ping")).Returns(true);

而不是:

class FooMock : IFoo {
  bool DoSomething(string input) {
    return input == "ping";
  }
}
mock = new FooMock();

后者更冗长,但似乎更灵活,更适合复杂的嘲笑。

3 个答案:

答案 0 :(得分:12)

你在你的例子中显示的更多是假的/存根而不是真正的模拟,如果你只想要一个依赖对象的预先行为,那么通常假的可能是比使用假的更好的选择。嘲弄框架。

马丁福勒有一篇规范的文章讨论了Mocks aren't Stubs的事实,我从中提出了以下段落:

  

这里的关键区别在于我们如何   验证订单是否正确   与它相互作用的东西   仓库。通过状态验证我们   通过断言来对抗   仓库的状态。模拟使用行为   验证,我们改为检查   查看订单是否正确   打电话给仓库。

基本上对于模拟,你通常打算检查你的测试方法如何对依赖项起作用 - 你的模拟有期望,你在方法运行后验证了这些期望。

你当然可以编写自己的个案嘲笑,但使用框架将节省你很多时间,提供更可读的测试并节省测试中的错误。

尤其如此,因为期望你变得越来越复杂,想象有一个方法来测试哪个特定的依赖类根据输入参数调用具有不同值的可变次数 - 这对于编写模拟来说会很复杂你自己,但使用一个好的模拟框架是微不足道的。

为了演示一些代码,想象一下这个PrintOrders方法(原谅这个愚蠢的例子):

public void PrintForeignOrders(List<Orders> orders)
{
    foreach(var order in orders)
    {
        if (order.IsForeign)
        {
            printer.PrintOrder(order.Number, order.Name);
        }
    }
}

你可能想要至少测试一下:

  • 当列表为空时,不会打印任何内容
  • 当有外国订单时,打印
  • 当有两个订单时,两个订单都打印出来(使用正确的号码和名称)

使用一个好的模拟框架,针对注入的打印机对象设置这些测试只需几次击键即可。

答案 1 :(得分:9)

每次更改界面时,都必须调整每个手动编码的模拟对象(实际上是存根),以便与新界面兼容。

因此,每次更改界面时,拥有大量手动编码的存根对象都会导致大量的测试代码维护。然而,由模拟框架创建的模拟将始终与更新的接口兼容。

答案 2 :(得分:7)

  

但是,我们似乎无法证明这一点   他们只是创造模拟的用法   我们每个人的实施课程   接口

这正是模拟框架将使您无法做到的事情 - 创建存根或模拟实现只是为了能够测试您的类的行为。

你所做的一切都没有错,如果你的项目可行,一定要坚持下去 - 对于很多人来说这似乎是一项非常平凡的任务,而这就是使用嘲弄的地方框架可以为您节省大量时间,并且还可以避免代码库膨胀,仅用于测试以及模拟实现中出现其他错误的可能性。