软件测试技术:面向开发之Mock
小坐标 2018-03-08 来源 : 阅读 1084 评论 0

摘要:本章主要详细讲解开发中的一些软件测试技术,今天所涉及到的就是其中之一——Mock,下面就请大家跟着小坐标一起了解他吧。

  本章主要详细讲解开发中的一些软件测试技术,今天所涉及到的就是其中之一——Mock,下面就请大家跟着小坐标一起了解他吧.

 1 什么是Mock?

在软件测试领域,Mock的意思是模拟,简单来说,就是通过某种技术手段模拟测试对象的行为,返回预先设计的结果。这里的关键词是预先设计,也就是说对于任意被测试的对象,可以根据具体测试场景的需要,返回特定的结果。打个比方,就像BBC纪录片里面的假企鹅,可以根据拍摄需要作出不同的反应。

 2 Mock有什么用?

理解了什么是Mock,再来看Mock有哪些用途。首先,Mock可以用来解除测试对象对外部服务的依赖(比如数据库,第三方接口等),使得测试用例可以独立运行。不管是传统的单体应用,还是现在流行的微服务,这点都特别重要,因为任何外部依赖的存在都会极大的限制测试用例的可迁移性和稳定性。可迁移性是指,如果要在一个新的测试环境中运行相同的测试用例,那么除了要保证测试对象自身能够正常运行,还要保证所有依赖的外部服务也能够被正常调用。稳定性是指,如果外部服务不可用,那么测试用例也可能会失败。通过Mock去除外部依赖之后,不管是测试用例的可迁移性还是稳定性,都能够上一个台阶。

 

软件测试技术:面向开发之Mock

Mock的第二个好处是替换外部服务调用,提升测试用例的运行速度。任何外部服务调用至少是跨进程级别的消耗,甚至是跨系统、跨网络的消耗,而Mock可以把消耗降低到进程内。比如原来一次秒级的网络请求,通过Mock可以降至毫秒级,整整3个数量级的差别。

Mock的第三个好处是提升测试效率。这里说的测试效率有两层含义。第一层含义是单位时间运行的测试用例数,这是运行速度提升带来的直接好处。而第二层含义是一个QE单位时间创建的测试用例数。如何理解这第二层含义呢?以单体应用为例,随着业务复杂度的上升,为了运行一个测试用例可能需要准备很多测试数据,与此同时还要尽量保证多个测试用例之间的测试数据互不干扰。为了做到这一点,QE往往需要花费大量的时间来维护一套可运行的测试数据。有了Mock之后,由于去除了测试用例

之间共享的数据库依赖,QE就可以针对每一个或者每一组测试用例设计一套独立的测试数据,从而很容易的做到不同测试用例之间的数据隔离性。而对于微服务,由于一个微服务可能级联依赖很多其他的微服务,运行一个测试用例甚至需要跨系统准备一套测试数据,如果没有Mock,基本上可以说是不可能的。因此,不管是单体应用还是微服务,有了Mock之后,QE就可以省去大量的准备测试数据的时间,专注于测试用例本身,自然也就提升了单人的测试效率。

 3 如何Mock?

说了这么多Mock的好处,那么究竟如何在测试中使用Mock呢?针对不同的测试场景,可以选择不同的Mock框架。

 3.1 Mockito

如果测试对象是一个方法,尤其是涉及数据库操作的方法,那么Mockito可能是最好的选择。作为使用最广泛的Mock框架,Mockito出于EasyMock而胜于EasyMock,乃至被默认集成进Spring Testing。其实现原理是,通过CGLib在运行时为每一个被Mock的类或者对象动态生成一个代理对象,返回预先设计的结果。集成Mockito的基本步骤是:

标记被Mock的类或者对象,生成代理对象

通过Mockito API定制代理对象的行为

调用代理对象的方法,获得预先设计的结果

下面是我GitHub上的示例工程里的一个例子:

 @RunWith(SpringRunner.class)
  @SpringBootTest
  public class SignonServiceTests {
  
    // 测试对象,一个服务类
    @Autowired
    private SignonService signonService;
  
    // 被Mock的类,被服务类所依赖的一个DAO类
    @MockBean
    private SignonDao dao;
  
    @Test
    public void testFindAll() {
        // SignonService#findAll()内部会调用SignonDao#findAll()
        // 如果不做定制,所有被Mock的类默认返回空
        List<Signon> signons = signonService.findAll();
        assertTrue(CollectionUtils.isEmpty(signons));
  
        // 定制返回结果
        Signon signon = new Signon();
        signon.setUsername("foo");
        when(dao.findAll()).thenReturn(Lists.newArrayList(signon));
  
        signons = signonService.findAll();
        // 验证返回结果和预先设计的结果一致
        assertEquals(1, signons.size());
        assertEquals("foo", signons.get(0).getUsername());
    }
}

从上面的测试用例可以看到,通过Mock服务类所依赖的DAO类,我们可以跳过所有的数据库操作,任意定制返回结果,从而专注于测试服务类内部的业务逻辑。这是传统的非Mock测试所难以实现的。

注意:Mockito不支持Mock私有方法或者静态方法,如果要Mock这类方法,可以使用PowerMock。

 3.2 WireMock

如果说Mocketo是瑞士军刀,可以Mock Everything,那么WireMock就是为微服务而生的倚天剑。和处在对象层的Mockito不同,WireMock针对的是API。假设有两个微服务,Service-A和Service-B,Service-A里的一个API(姑且称为API-1),依赖于Service-B,那么使用传统的测试方法,测试API-1时必然需要同时启动Service-B。如果使用WireMock,那么就可以在Service-A端Mock所有依赖的Service-B的API,从而去掉Service-B这个外部依赖。

同样看一个我GitHub上的示例工程里的一个例子:

 @RunWith(SpringRunner.class)
 @WebMvcTest(VacationController.class)
   public class VacationControllerTests {
   
     // Mock被依赖的另一个微服务
     @Rule
     public WireMockRule wireMockRule = new WireMockRule(3001);
     
     @Autowired
     private MockMvc mockMvc;
   
     @Autowired
     private ObjectMapper objectMapper;
   
     @Before
     public void before() throws JsonProcessingException {
         // 定制返回结果
         JsonResult<Boolean> expected = JsonResult.ok(true);
         stubFor(get(urlPathEqualTo("/api/vacation/isWeekend"))
                 .willReturn(aResponse()
                         .withStatus(OK.value())
                         .withHeader(CONTENT_TYPE, APPLICATION_JSON_UTF8_VALUE)
                         .withBody(objectMapper.writeValueAsString(expected))));
     }
   
     @Test
     public void testIsWeekendProxy() throws Exception {
         // 构造请求参数
         VacationRequest request = new VacationRequest();
         request.setType(PERSONAL);
         OffsetDateTime lastSunday = OffsetDateTime.now().with(TemporalAdjusters.previous(SUNDAY));
         request.setStart(lastSunday);
         request.setEnd(lastSunday.plusDays(1));
   
         MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/vacation/isWeekend");
         request.toMap().forEach((k, v) -> builder.param(k, v));
         JsonResult<Boolean> expected = JsonResult.ok(true);
   
         mockMvc.perform(builder)
                 // 验证返回结果和预先设计的结果一致
                 .andExpect(status().isOk())
                 .andExpect(content().contentType(APPLICATION_JSON_UTF8))
                 .andExpect(content().string(objectMapper.writeValueAsString(expected)));
     }
 }

和Mockito类似,在测试用例中集成WireMock的基本步骤是:

声明代理服务,以替代被Mock的微服务

通过WireMock API定制代理服务的返回结果

调用代理服务,获得预先设计的结果

值得一提的是,除了API方式的集成,WireMock还支持以Jar包的形式独立运行,从配置文件中加载预先设计的响应结果,以替代被Mock的微服务。更多信息可以参阅官方文档。

其他类似的Mock API的框架还有OkHttp的mockwebserver,moco和mockserver。mockwebserver也属于嵌入式Mock框架的范畴,但功能过于简单。moco,mockserver虽然功能完善,但需要独立部署,和WireMock相比不具有优势。

 4 小结

以上,关于软件测试的全部内容讲解完毕啦,欢迎大家继续关注!更多关于软件测试的干货请关注职坐标软件测试频道!希望这篇软件测试文章可以帮助到你。总之,同学们,你想要的职坐标软件测试频道都能找到!
本文由职坐标整理并发布,希望对同学们学习软件测试的知识有所帮助。了解更多详情请关注职坐标软件测试频道!

本文由 @小坐标 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程