下载APP
关闭
讲堂
部落
算法训练营
Python 进阶训练营
企业服务
极客商城
客户端下载
兑换中心
渠道合作
推荐作者

09 | 微服务接口:怎么用Mock解决混乱的调用关系?

2020-02-19 陈磊
接口测试实战课
进入课程

讲述:陈磊

时长11:22大小9.12M

你好,我是陈磊。
欢迎你继续和我一起学习接口测试,到目前为止,我们已经学习了接口测试的逻辑模拟,也就是测试辅助工具和测试脚本代码,也学习了如何选取和通过代码调用测试参数,掌握了这些内容,你就算是一个接口测试的老手了。无论你的被测接口是一个你熟悉的协议,还是一个陌生的协议,它们都不会耽误你的工作进度了。
这节课是我们专栏的最后一节课,我想给你讲一讲关于微服务的接口测试。
现如今在我的工作中,我主要面对的就是微服务测试,每个服务都是 RESTful 接口。在最开始的微服务改造过程中,我的测试其实比之前的业务测试更容易,每一个接口通过测试框架来编写测试脚本就可以完成执行了,而且一次写完后再通过平台调用,也显得很轻松。但是这种美好的场景并没有持续多久。为什么呢? 你先听听我的故事。

微服务下混乱的调用关系

开发团队开始采用微服务架构开发系统的时候,我的测试团队也开始同步学习对应的测试技术,我也像从前一样,逐步封装自己的测试框架,并且采用 Postman 和 Python 代码,完成接口测试脚本的快速积累,同时引入了参数类,完成了 Excel 参数的封装调用。
在开始的一些项目中,只要开发工程师提交了代码仓库主干的合并请求后,除去代码的静态扫描外,持续集成平台会自动调取一个开源的智能化单元测试框架,来完成单元测试,通过后它会自动部署被测系统,然后再执行测试脚本,这整个流程全部是流水线自动驱动完成的。
一般来说,开发工程师在开发前期就已经定义好了微服务接口,测试工程师和开发工程师几乎是同步开始进行各自的开发任务。但是,这种和谐的工作场景很快就被蜘蛛网一样的微服务调用关系给破坏了,几乎所有的项目都会出现相互依赖的关系,比如说服务 A 依赖服务 B,服务 B 依赖服务 C,如下图所示:
这种混乱主要体现在:
当持续集成流水线部署服务 A 的时候,由于对应的开发工程师团队也在做同步改造,导致测试环境的服务 B 不可用;
由于服务 B 依赖服务 C,而服务 C 还没有开发完成,导致即使服务 A 和服务 B 都没问题,但也没有办法完成服务 A 的接口测试。
其实这种服务 A 依赖服务 B,服务 B 依赖服务 C 的依赖方式还算简单,还有更多微服务随着开发越来越复杂,服务之间的调用关系就像蜘蛛网一样错乱,让你摸不清外部依赖到底有几层,以及一个接口到底依赖了几个外部接口。
这就导致了虽然被测系统已经开发完成,测试脚本也准备就绪,但是测试工作就是没办法进行的悲惨结局。面对这种局面,我当时心里确实很不舒服,因为自己做了那么多努力,到头来却被一个不是由自己负责的服务卡住了工作进度,这感觉就像是用尽了全身的力气,却一拳打到了棉花上,自己有再多的劲儿也没处使。

Mock 框架的抉择:用什么实现服务 B 的替身

那作为测试工程师,面对这样的情形,我们该怎么办呢?
我当时想到的就是使用 Mock 服务。其实 Mock 服务是一个错误的说法,关于这一点我推荐你看一下 Martin Flower 的这篇叫做TestDouble的文章,一般我们将 TestDouble 服务叫做测试替身,但是如今的国内业界里,绝大部分人已经习惯了叫 Mock 服务,因此在这里我们也还是叫 Mock 服务。
针对混乱的调用关系,我的思路是:我的被测服务就是服务 A,那么我不用管服务 B 是不是好用,我只要保障服务 A 能够走完流程,就可以完成接口测试任务了。循着这个思路,我只要用 Mock 服务伪装成服务 B 就万事大吉了,我也不用再关心服务 B 到底调用了多少服务。
但是在选取 Mock 服务框架时,我又面临着一个抉择,那就是用什么来实现服务 B 的替身。现在可以实现 Mock 服务的框架特别多,但绝大部分都要求你有很好的代码基础,每做一个 Mock 服务其实就是做了一个简单的服务 B,不同的是,它不需要实现原有服务 B 负载的处理逻辑,只要能按服务 B 的处理逻辑给出对应返回就可以了。
因此,有些团队也会把这样的服务叫做挡板系统,这个名字很形象。也就是说,我给了 Mock 服务 B 的请求参数,它只要按照约定好的返回给我参数就可以了,至于一系列其它验证或者微服务调用,都不在 Mock 服务的设计内,这就像你对着墙打乒乓球一样,墙是你假设的对手,会把你打过去的球挡回来,而你要做的就是接住墙挡回给你的球。
那么,到底应该怎么选择 Mock 服务框架呢?
首先,你要基于自己和团队的技术栈来做选择,这决定了你完成服务 B 替身的速度。你要知道,无论服务 B 的替身做得多么完美,它只是一个 Mock,它存在的意义就是帮助你快速完成服务 A 的接口测试工作,因此,选择一个学习成本低、上手快并且完全适合你自己技术栈的 Mock 框架,能让你的测试工作事半功倍。
其次,你要让写好的 Mock 服务容易修改和维护。Mock 服务就是一个在测试过程中替代服务 B 的替身,就和拍电影时的替身演员一样,替身演员可能有好几个,需要在不同地方拍摄不同的电影片段。Mock 服务可能只有一个,也有可能有好几个,为了不同的调用或者测试而存在。但是,Mock 服务会随着服务 B 的变化而变化,如果服务 B 的请求参数和返回参数有变化,那么 Mock 服务也要能快速完成修改,并且能马上发挥作用。因此,一个非常容易维护的 Mock 服务框架,才更能马上快速投入使用,快速发挥作用。
如果你的团队技术基础很好,开发能力很强,那么我建议你用对应语言的 Mock 框架,例如 Java 语言的Mockito 框架和 Python 语言的mock 框架
如果你的团队技术基础相对比较薄弱,那么我推荐你看看moco,这个框架在开发 Mock 服务的时候,提供了一种不需要任何编程语言的方式,你可以通过撰写它约束的 Json 建立服务,并通过命令启动对应的服务,这就可以快速开发和启动运行你需要的 Mock 服务。
更重要的是,Json 格式的数据文件可以独立完成 Mock 的服务设计,而且 Json 的学习成本和 Python 语言相比,就如同小学一年级的数学和高中数学之间的难度差距一样,就更别说和犹如高等数学的 Java 语言相比较了。如果你想详细的学习 moco,可以直接去它在 Github 上的项目空间,那里有详细的使用说明和示例代码。

我的 Mock 服务设计经验

在选择好 Mock 框架后,你就可以酣畅淋漓地完成各个外部依赖服务的解耦工作了,但是关于 Mock 服务,我还想告诉你一些我的设计经验。
首先,简单是第一要素。无论原服务 B 处理了多么复杂的业务流程,你在设计服务 B 的 Mock 服务时,只要关心服务 B 可以处理几种类型的参数组合,对应的服务都会返回什么样的参数就可以了。这样你就能快速抓住 Mock 服务 B 的设计核心,也能快速完成 Mock 服务 B 的开发。
其次,处理速度比完美的 Mock 服务更重要。一个 Mock 服务要能按照原服务正确又快速地返回参数,你不需要把大量的时间都浪费在 Mock 服务的调用上,它只是用来辅助你完成接口测试的一个手段。你需要让它像打在墙上的乒乓球一样,一触到墙面马上就反弹回来;而不能把球打出后,需要去喝个茶或者坐下休息一会,才能收到反弹回来的球。
如果你的 Mock 服务很耗时,你在只有一个两个服务时,可能影响还不是很明显,但如果你同时有多个 Mock 服务,或者需要用 Mock 服务完成性能测试的时候,这就会变成一个很严重的问题,后续会引发强烈的“蝴蝶效应”,使得整个被测接口的响应速度越来越慢。因此你要建立一套快速的 Mock 服务,尽最大可能不让 Mock 服务占据系统的调用时间。
最后,你的 Mock 服务要能轻量化启动,并且容易销毁。你要时刻注意,Mock 服务只是一个辅助服务,因此,任何一个团队都不希望启动一个 Mock 服务需要等待 5 分钟,或者需要 100M 的内存。它要能快速启动、容易修改并且方便迁移。既然 Mock 服务的定位是轻量化的辅助服务,那么它也要容易销毁,以便你在完成测试后,可以快速且便捷地释放它所占据的资源。

总结

微服务现在已经铺天盖地而来,尤其在中台化战略的推动下,业务中台服务的依赖关系会越来越复杂,并且随着团队内微服务数量越来越多,每个测试团队面临的被测系统都会是一团乱麻,很容易找不到头绪。
为了解决由于微服务间相互依赖而导致的混乱的系统调用关系,我建议你尽快掌握一个 Mock 服务框架,这样可以让你在混乱中理清思路,快速进行接口测试,交付高质量的项目。
最后我要提醒你的是,选择 Mock 的技术栈与选择测试框架的技术栈还是有些区别的,在选择 Mock 技术栈时,你重点要考虑的是学习成本,把学习成本降到最低,才是选择 Mock 框架的首要关注点。而且你不只要关注自己的学习成本,也要关注你所在团队的学习成本,因为现在每个项目都有可能需要 Mock 服务,这个时候,就要求每一个项目的测试工程师都具备自己独立建设 Mock 服务的能力,在 Mock 服务的技术选型上,还是要以团队整体的技术栈为基础,以自己的技术为参考进行选型。

思考题

这节课我讲了在微服务混乱的外部调用下,使用 Mock 外部接口完成被测接口的测试工作,文中我也给你推荐了一个快速入门的 Mock 工具,那么你在工作中,有没有遇见过被测系统因为外部依赖,而不得不阻塞项目进度的时候呢?你又是怎么解决的呢?
我是陈磊,欢迎你在留言区留言分享你的观点,如果这篇文章让你有新的启发,也欢迎你把文章分享给你的朋友,我们一起沟通探讨。
unpreview
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
上一篇
08 | 测试数据:是不是可以把所有的参数都保存到Excel中?
下一篇
结束语 | 如何成为一名优秀的测试工程师?
 写留言

精选留言(10)

  • 2020-02-19
    看到这篇,测试同学已经和程序员不相上下了。作为一名Java程序员,收获很多,十分感谢!也有几个问题请教您:

    1. 测试的微服务混用通信方式(HTTP同步、消息中间件异步),这时除了使用mock服务器测试,是否也需要搭建“消息中间件”才能完成测试?还是可以用HTTP代替?

    2. 老师在工作中是否遇到过对底层工具、核心类代码做“微基准测试”?如果有,一般哪部分代码需要进行此类测试?

    3. 能否分享一下接口测试时常用的异常数据?例如:类型不一致、空字符串、超出长度、必传参数为空,SQL注入语句、传多个无效参数占用带宽、POST提交时包含10M+甚至更大容量的信息等。
    展开

    作者回复: 您好,您的问题我一一回答,希望有些帮助:
    1. 测试的微服务混用通信方式(HTTP同步、消息中间件异步),这时除了使用mock服务器测试,是否也需要搭建“消息中间件”才能完成测试?还是可以用HTTP代替?
    答如果仅仅是为了做Mock可以使用http代替,这里并不是说用MQ不好,如果这个事情是测试来做,很多测试并无法独立完成,因此这里以解决问题为根本出发点。如果测试团队技术比较好,那么使用MQ会更好。

    2. 老师在工作中是否遇到过对底层工具、核心类代码做“微基准测试”?如果有,一般哪部分代码需要进行此类测试?
    答:接口测试并不适合对底层工具或者核心类做测试,这部分只适合对外提供或者对内提供调用的接口,如果是对核心代码,建议还是使用单元测试,可是采用EvoSuite这个框架做一次批量单元测试。EvoSuite可以在我的Blog https://blog.csdn.net/crisschan找到,我对这个开源框架的使用有一些心德以及一些坑我都有记录。
    3. 能否分享一下接口测试时常用的异常数据?例如:类型不一致、空字符串、超出长度、必传参数为空,SQL注入语句、传多个无效参数占用带宽、POST提交时包含10M+甚至更大容量的信息等。
    答:异常我比较建议最开始使用边界值,例如整型的就包含了1,1.1,-1,-1.1,“asb”,null,True。先确定类型以及接口的程序中的约束,然后确定边界值,最后在完成测试入参组合,组合可以使用正交试验的方法,正交计算如果不是很熟悉(终归这个测试使用比较多)可以参考一下我写的正交试验的计算库:https://github.com/crisschan/TamanduaOATs,写的并没有那么好,是C++写的,但是可以使用,在您这样一个开发面前我有点班门弄斧。参数中是不是包含大容量的信息这些还是要看具体的参数要求,并不是一概而论。

    谢谢您

    2
    6
  • 2020-02-23
    老师您好,看见有同仁也在请教mock服务器定向访问的问题,关于这块我也想多请教一下~之前做过一个跟三方合作项目的自动化,在自动化的时候将三方接口都给mock掉了,在运维的帮助下,搭建了一个mock服务器,当时运维就给了我几个根路由,我只用在接口调用的时候带上相应的根路由+path,发出的请求就会自动发送到mock服务器上,而不去调用三方的接口。请问为啥在未改动相应开发代码的前提下,可以实现请求的转移的呢?或者有无相应的资料可以推荐我自行去查阅,感谢🙏
    展开

    作者回复: 在不更改代码的情况下有些外部依赖的服务可以通过统一的proxy转发,这个统一配置可以完成。您可以学习一下服务端的proxy的使用。另外还可以通过统一配置服务修改的,开发在代码中的路由是通过配置完成的,因此替换配置也可以完成。具体那种可行还要SUT,因此您可以看一下服务端proxy或者统一配置。应该还有其他方法,不过都是通过一下代码的配置或者转发完成的,目前还没有看到过可以一劳永逸的工具,这都要依赖于开发的系统设计,因此选择那种使用被测系统决定的。

  • 2020-02-23
    我在工作中也遇到过使用Mock服务测试的情况;我们的软件是为生产产线服务的,代码分业务层代码和物理层代码,物理层的代码主要需要和机器仪器交互,由于生产环境紧张,所以我们需要验充分验证业务层的代码逻辑,就需要模拟物理层的接口返回。我们先在生产环境用Fiddler抓取接口返回数据,然后使用自己的MOckf服务完全模拟物理层的返回数据充分验证业务层的代码。 还有个问题想问问老师,mock服务和打桩测试有什么区别?
    展开

    作者回复: 在中文中应该是一个意思了,但是这种叫法确实不对,文章有Martin的一个文章 里面详细介绍了test double的区别,有一种叫做stub就是您说的打桩,和mock是有区别的,但是在国内这概念一点混淆。

  • 2020-02-23
    打卡~
    展开

    作者回复: 谢谢

  • 2020-02-22
    老师你好,微服务中mock服务搭建好后,怎么路由到“替身”服务呢?面对微服务的主控,服务注册这些都不是很懂。
    展开

    作者回复: mock服务要让系统调用还是需要开发协助的,一般是让开发做一个独立的配置,方便切换

  • 2020-02-22
    文中有提到一款智能化单元测试框架,能请问下该框架是哪个吗?

    作者回复: EvoSuite,是我稍微熟悉一点的,具体怎么使用已经使用中有什么坑,可以看一下https://blog.csdn.net/crisschan我的博客中里面是有详细的介绍的。

    1
  • 2020-02-21
    看到"挡板"这两个字,一下子勾起了我16和17年在一家第三方支付公司的经历,那时候也是需要依赖第三方,然后测试的时候需要让下游服务加个"挡板",那个时候听着觉得好玩儿,后面才慢慢知道就是返回假数据,也就是mock
    展开

    作者回复: 挡板我也是在银行或者第三方支付行业听说的。我们有了类似的经历。

  • 2020-02-20
    有个很头疼的问题想请假老师。 push推送 能mock吗? 公司的推送平台经常出问题,而且最近在家办公,测试环境push更是难通。如果不能mock,有什么解决的方案或方向吗?求指明,谢谢!

    作者回复: 如果push服务是你的SUT那就不能用替代服务。如果是不是,那么可以mock,你只需要知道push服务等方面返回就好了,模拟调用push服务,并返回成功推动结果就好了,那么是不是推送出去了不重要。

  • 2020-02-20
    老师好,没用过mock,这块空白,所以几个小问题请教下,
    1.我怎么样主动发现被测接口是有外部系统依赖的呢,有时候我并不知道存在这样的依赖关系
    2.如果我依赖的不是一个另一个系统,而是一个数据库,这个也可以mock掉?
    3.对于那些有外部依赖的被测接口,不同的接口依赖的返回结构也是不同的,怎么去描述这种结构呢?而且我在测试A接口的时候,并不清楚它依赖的B接口的接口信息啊
    4.总感觉,这样会带来零一份开销,工作量也很大
    5.有些依赖环境是OK的,有些不OK,那么我们是统一采用mock,还是部分采用mock
    6.后续所有系统开发完毕后,还需要对当初使用mock的接口重新测试一下吗?(只是担心)
    展开

    作者回复: 要做到mock服务,需要测试工程师和系统架构师、开发工程师有深入的交流,team内部高度的合作,解耦是为了接触外部问题,不是团队内部问题。这个mock服务既可以为测试使用,也可以提供给开发使用的。

  • 2020-02-19
    似乎问题很多都已经不属于接口测试的问题了😖
    展开

    作者回复: 恩,相信很多人都开始觉得测试做了很多不是测试的事情,但是随着devops的出现,做事情的边界已经和之前不一样了,现在开发、测试运维是一个team,大家统一的目标是交付一个高质量的项目,因此目前这些内容还是属于测试的工作。