模拟对象的目的是什么?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (10)

我刚开始进行单元测试,我不断地听到“模拟对象”这个词到处抛来抛去。用外行的话说,有人能解释什么是模拟对象,以及它们在编写单元测试时通常用于什么?

提问于
用户回答回答于

想象一下这个系统的单元测试:

cook <- waiter <- customer

通常很容易设想测试一个低级别的组件,比如cook:

cook <- test driver

测试驱动程序只需订购不同的菜,并验证厨师为每个订单返回正确的菜。

要测试一个中间部件,比如服务生,利用其他组件的行为就更难了。天真的测试人员可能会像我们测试厨师组件一样测试服务员组件:

cook <- waiter <- test driver

测试司机会点不同的菜,并确保服务员返回正确的菜。不幸的是,这意味着对服务员组件的测试可能取决于厨师组件的正确行为。如果厨师组件有任何不友好的测试特性,比如不确定的行为(菜单中包括师的惊喜作为一道菜)、大量的依赖(没有所有的工作人员就不会做饭),或者大量的资源(有些菜肴需要昂贵的配料或需要一个小时的时间),这种依赖性就更糟糕了。

由于这是一个服务员测试,理想情况下,我们想测试的只是服务生,而不是厨师。具体来说,我们要确保服务员将顾客的订单正确地传达给厨师,并将厨师的食物正确地送到顾客手中。

    -----------------------
   |                       |
   v                       |
test cook <- waiter <- test driver

在这里,测试厨师是“同谋”与测试司机。理想情况下,测试系统的设计使测试厨师可以很容易地被替换与服务生一起工作,而不改变生产代码。

模拟对象

现在,可以通过不同的方式实现测试厨师(Test Double):

  • 一个冒充厨师的人用冷冻晚餐和微波炉假装是个厨师,
  • 存根厨师-一个热狗供应商,总是给你热狗,无论你点什么,或
  • 一个模拟厨师--一个卧底警察,在一次捕杀行动中假装是个厨师。
    -----------------------
   |                       |
   v                       |
mock cook <- waiter <- test driver

在单元测试中,一个很大的部分是服务生如何与厨师进行交互。一种基于模拟的方法侧重于充分指定什么是正确的交互,并在它出错时进行检测。

模拟对象预先知道在测试期间应该发生什么(例如,它的方法调用将被调用,等等),而模拟对象知道它应该如何反应(例如,提供什么返回值)。模拟将指示实际发生的情况是否与预期发生的情况不同。可以从零开始为每个测试用例创建自定义模拟对象,以执行该测试用例的预期行为,但是模拟框架努力允许这样的行为规范在测试用例中直接明确和容易地指示。

围绕基于模拟的测试的对话可能如下所示:

测试驱动器模拟厨师期待热狗的订购,并给他这个假热狗作为回应。测试驱动器(冒充顾客)侍者我想要热狗侍者模拟厨师请给我一条热狗模拟厨师侍者点菜:1只热狗准备好了(给服务生吃假热狗)侍者测试驱动器这是你的热狗(给测试司机提供假热狗)测试驱动器测试成功!

但既然我们的侍者是新来的,这就是可能发生的事:

测试驱动器模拟厨师期待热狗的订购,并给他这个假热狗作为回应。测试驱动器(冒充顾客)侍者我想要热狗侍者模拟厨师请给我一个汉堡模拟厨师停止测试:有人告诉我要点热狗!测试驱动器注意到问题:考试失败了!-服务生改变了命令

测试驱动器模拟厨师期待热狗的订购,并给他这个假热狗作为回应。测试驱动器(冒充顾客)侍者我想要热狗侍者模拟厨师请给我一条热狗模拟厨师侍者点菜:1只热狗准备好了(给服务生吃假热狗)侍者测试驱动器这是你的炸薯条(给你从其他一些订单中拿来的炸薯条来测试司机)测试驱动器意料之外的炸薯条:测试失败了!服务生把菜放错了。

用户回答回答于

模拟对象是替代真实对象的对象。在面向对象的编程中,模拟对象是模拟对象,以受控的方式模拟真实对象的行为。

计算机程序员通常创建一个模拟对象来测试其他对象的行为,就像汽车设计师使用碰撞测试假人来模拟人在车辆碰撞中的动态行为一样。

模拟对象允许设置测试场景,而不需要占用大型、笨重的资源,如数据库。可以使用单元测试中的模拟对象来模拟数据库,而不是调用数据库进行测试。这使摆脱了不得不设置和删除真正的数据库的负担,只需测试类中的单个方法。

“Mock”一词有时与“Stub”互换使用。从本质上说,模拟是一个存根对象,它还包括对被测试对象/方法的适当行为的期望(即“断言”)。

例如:

class OrderInteractionTester...
  public void testOrderSendsMailIfUnfilled() {
    Order order = new Order(TALISKER, 51);
    Mock warehouse = mock(Warehouse.class);
    Mock mailer = mock(MailService.class);
    order.setMailer((MailService) mailer.proxy());

    mailer.expects(once()).method("send");
    warehouse.expects(once()).method("hasInventory")
      .withAnyArguments()
      .will(returnValue(false));

    order.fill((Warehouse) warehouse.proxy());
  }
}

注意,warehousemailer模拟对象是用预期的结果编写的。

扫码关注云+社区