1

一、Mock相关概念介绍

1、mock的概念

WHAT--定义
Mock测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法(来自百度百科)。通俗点讲,就是:做个假对象、响应来实现某种特定场景的测试。

WHY--使用原因
(1)底层数据复杂,依赖严重等情况,按照真实的场景去使用,耗时、成本较大等;
(2)依赖的底层单元尚未开发完成,或现有资源难以满足当前模块的测试需求;
(3)一些特定的异常场景,较难复现等;

WHEN & WHERE--使用场景
--其实WHY里面,已经回答了这个问题。以下两条也是使用原则,不必每个单元测试都使用Mock。
(1)单元测试/接口测试中测试对象依赖其他对象,这些对象的构造复杂、耗时或者根本无法构
造(未交付);
----简单的场景,没必要使用,可能会耗时更多。异常场景、复杂场景、影响工作效率场景等建议使用。
(2)我们只测试对象内部逻辑的质量,不关心依赖对象的逻辑正确性和稳定性;
----即:底层的代码已确保由底层保障,不需要调用方关心。

2、测试哑元(Dummy)

Dummy Object,测试代码需要Dummy Object是因为有了它才能通过编译,测试才能跑起来,但其实测试中可能根本就用不到它。

eg1:

clipboard.png

例如,创建BlogService 需要 BlogDao,但你可能测试BlogService 的一个方法,它根本就没用到BlogDao。此时,你可以用 new BlogService(new NullBlogDao()), NullBlogDao 就是Dummy Object,因为它的存在只是为了通过编译,它根本就不参与测试。

eg2:
clipboard.png

图中main函数,需要统计对象个数。需要实例PersonImpl的对象,然而PersonImpl是Person接口的实现类,所以必须实现Person接口的抽象方法study(),所以此时PersonImpl类中的实现方法在main函数中并没有起到任何作用,但是必须写上,确保程序不报错,这时候study()就可以理解为一个Dummy。

3、测试桩(Stub)

clipboard.png

Stub 参与测试, 但你不在乎它是何时何地以何种方式参与测试的,它的存在是为了让测试跑起来,在测试过程中产生的调用提供预备好的应答,通常不应答计划之外的任何事,非常常见的情况是你需要它提供一些返回值。例如,你可以用HttpContextStub 来代替真正的HttpContext, 用它提供例如SessionId, ResquestParameter 之类的值。你的测试可能会用到这些值,但你不会去验证是不是getSessionId() 被调用了,更不会去验证它是何时何地以何种方式被调用的。

clipboard.png

如图中,跳过查mysql的数据操作,模拟测试桩来代替查数据库的步骤,不需要关心查mysql的操作,只关注数据按照这个格式返回。

4、测试间谍(Spy)

clipboard.png

spy 可以理解为侦查,它负责汇报情况,持续追踪什么方法被调用了,以及调用过程中传递了哪些参数。你能用它来实现测试断言,比如一个特定的方法是否被调用或者是否使用正确的参数调用。当你需要测试两个对象间的某些协议或者关系时会非常有用。

public void testRemoveFlightLogging_recordingTestStub()
            throws Exception {
      // Fixture setup
      FlightDto expectedFlightDto = createAnUnregFlight();
      FlightManagementFacade facade =  new FlightManagementFacadeImpl();
      // Test Double setup
      AuditLogSpy logSpy = new AuditLogSpy();
      facade.setAuditLog(logSpy);
      // Exercise
      facade.removeFlight(expectedFlightDto.getFlightNumber());
      // Verify state
      assertFalse("flight still exists after being removed",
                  facade.flightExists( expectedFlightDto.
                                            getFlightNumber()));
      // Verify indirect outputs using retrieval interface of spy
      assertEquals("number of calls", 1, logSpy.getNumberOfCalls());
      assertEquals("action code", Helper.REMOVE_FLIGHT_ACTION_CODE,
                   logSpy.getActionCode());
      assertEquals("date", helper.getTodaysDateWithoutTime(),
                   logSpy.getDate());
      assertEquals("user", Helper.TEST_USER_NAME, logSpy.getUser());
      assertEquals("detail", expectedFlightDto.getFlightNumber(),
                   logSpy.getDetail());
}

5、仿冒对象(Fake Object)

clipboard.png

fake 是一个具备完整功能实现和行为的对象,行为上来说它和这个类型的真实对象上一样,但不同于它所模拟的类,它使测试变得更加容易。一个典型的例子是使用内存中的数据库来生成一个数据持久化对象,而不是去访问一个真正的生产环境的数据库。

经常,我们会把Fake Object和Test Stub搞混,因为它们都和外部没有交互,对内部的输入输出也不进行验证。不同的是,Fake Object并不关注SUT内部的间接输入(indirect inputs)或间接输出(indirect outputs),它仅仅是用来替代一个实际的对象,并且拥有几乎和实际对象一样的功能,保证SUT能够正常工作。实际对象过分依赖外部环境,Fake Object可以减少这样的依赖。

6、仿制对象(Mock object)

clipboard.png

mock 与 spy 类似,但在使用上有些许不同。spy 追踪所有的方法调用,并在事后让你写断言,而 mock 通常需要你事先设定期望。你告诉它你期望发生什么,然后执行测试代码并验证最后的结果与事先定义的期望是否一致。


Sunflower
171 声望8 粉丝

Keep Hungery!


引用和评论

0 条评论