为什么要使用模拟框架?

Law*_*eld 5 .net c# rhino-mocks moq mocking

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

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

为什么这样:

var mock = new Mock<IFoo>();
mock.Setup(foo => foo.DoSomething("ping")).Returns(true);
Run Code Online (Sandbox Code Playgroud)

而不是这个:

class FooMock : IFoo {
  bool DoSomething(string input) {
    return input == "ping";
  }
}
mock = new FooMock();
Run Code Online (Sandbox Code Playgroud)

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

Dav*_*all 12

您在示例中显示的更多是假/存根而不是真正的模拟,如果您只想要从依赖对象进行预先封装的行为,那么通常假冒伪劣可能是比使用模拟框架更好的选择.

Martin Fowler有一篇规范的文章讨论了Mocks不是Stubs的事实,我从中提出了以下段落:

这里的关键区别在于我们如何验证订单在与仓库的交互中做了正确的事情.通过状态验证,我们通过对仓库状态的断言来做到这一点.模拟使用行为验证,我们检查订单是否在仓库上进行了正确的调用.

基本上对于模拟,您通常打算检查您的测试方法如何对依赖项进行操作 - 您的模拟有期望,并在方法运行后验证这些期望.

您当然可以编写自己的案例模拟,但使用框架将节省您大量时间,提供更可读的测试并节省测试中的错误.

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

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

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

你可能想要测试至少:

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

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


Pet*_*ete 9

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

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


Bro*_*ass 7

但是,我们似乎无法证明它们仅用于为每个接口创建Mock实现类.

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

无论如何,你所做的事情都没有错,如果你的项目可行,那么一定要坚持下去 - 对于许多人来说,这似乎是一项非常平凡的任务,这就是使用模拟框架可以节省的地方您需要花费大量时间,并且还要避免代码库膨胀,仅用于测试以及模拟实现中可能出现的其他错误.