09 微服务接口:怎么用Mock解决混乱的调用关系?
你好,我是陈磊。
欢迎你继续和我一起学习接口测试,到目前为止,我们已经学习了接口测试的逻辑模拟,也就是测试辅助工具和测试脚本代码,也学习了如何选取和通过代码调用测试参数,掌握了这些内容,你就算是一个接口测试的老手了。无论你的被测接口是一个你熟悉的协议,还是一个陌生的协议,它们都不会耽误你的工作进度了。
这节课是我们专栏的最后一节课,我想给你讲一讲关于微服务的接口测试。
现如今在我的工作中,我主要面对的就是微服务测试,每个服务都是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工具,那么你在工作中,有没有遇见过被测系统因为外部依赖,而不得不阻塞项目进度的时候呢?你又是怎么解决的呢?
我是陈磊,欢迎你在留言区留言分享你的观点,如果这篇文章让你有新的启发,也欢迎你把文章分享给你的朋友,我们一起沟通探讨。
- aoe 👍(24) 💬(3)
看到这篇,测试同学已经和程序员不相上下了。作为一名Java程序员,收获很多,十分感谢!也有几个问题请教您: 1. 测试的微服务混用通信方式(HTTP同步、消息中间件异步),这时除了使用mock服务器测试,是否也需要搭建“消息中间件”才能完成测试?还是可以用HTTP代替? 2. 老师在工作中是否遇到过对底层工具、核心类代码做“微基准测试”?如果有,一般哪部分代码需要进行此类测试? 3. 能否分享一下接口测试时常用的异常数据?例如:类型不一致、空字符串、超出长度、必传参数为空,SQL注入语句、传多个无效参数占用带宽、POST提交时包含10M+甚至更大容量的信息等。
2020-02-19 - 钱 👍(9) 💬(2)
OK,最后一节小结一下: 1:测试的痛点? 1-1:测试环境的搭建,微痛,一般都易解决 1-2:测试工具的搭建,稍痛,有现场工具也可以自己弄,关键是对接口的入参、出参、自动化的认识和积累,这是本专栏重点阐述和解决的问题 1-3:测试业务的积累,这个是一个针对简单业务不存在的问题,对于复杂业务需要不断积累的问题,对于接口测试比较难克服的问题,输入A返回B怎么判断这是一个OK的用例?复杂的接口,不敢说千变万化,不过一个接口可以有十几二十几中的输入又有十几二十几重的响应是很正常的,不了解业务基本判断不出来是否OK。这个每个企业的业务不同复杂度不同,不能一概而论,除了自己多和产品、研发以及其他了解的人多沟通,自己多思考多总结多积累,好像没有什么好法子,当然,如果项目文档比较完善也在持续跟进到是一种捷径 1-4:测试依赖问题,包括专栏里提到的外部服务依赖,另外就是测试数据依赖,目前我的经验是为了提高响应速度,微服务是依赖缓存数据进行计算的,不过缓存数据并不是从数据库直接放进去的需要经过复杂的计算,这种情况会到是测试链条拉长,需要配置数据刷新缓存,然后才能开始接口测试,有于业务本身的复杂性,不理解业务其实就是没法开展的。这个专栏里并未提到,我们目前的做法是隔离研发和厕所环境,测试环境的数据保持纯洁性,然后再进行定时任务的刷新,使依赖数据可以一直维持。 1-5:性能测试,接口测试功能OK,仅是其一,性能也很重要。也是接口测试内容,这个具体也有一些具体的方法和工具。并且有些问题仅通过功能测试是难以发现的,比如:线程安全问题,代码逻辑导致的性能差的问题。 2:专栏讲的核心内容? 2-1:利用工具提高效率,有现成用现成,没有就自己造 2-2:工具怎么造,封装、抽象、迭代,提高编码能力 3:有些收获,了解测试或者说接口测试思路,主要是时刻保持工具可复用化的观点。这个需要,工作生活中,养成这样的习惯。
2020-02-26 - 每天晒白牙 👍(6) 💬(1)
看到"挡板"这两个字,一下子勾起了我16和17年在一家第三方支付公司的经历,那时候也是需要依赖第三方,然后测试的时候需要让下游服务加个"挡板",那个时候听着觉得好玩儿,后面才慢慢知道就是返回假数据,也就是mock
2020-02-21 - VeryYoung 👍(3) 💬(2)
文中有提到一款智能化单元测试框架,能请问下该框架是哪个吗?
2020-02-22 - 蜡笔小新爱看书 👍(2) 💬(2)
Mock服务,可以理解为Charles的Map Local功能么?
2021-01-01 - sincoolvip 👍(2) 💬(1)
快看完了再由衷的评价,此课程作为一个指引 ,是非常好的,可以看得出作者的测试开发功底,对于有基础的同学来讲,一个指引就够了。所以还是挺好的一个课程。
2020-05-12 - ZeroIce 👍(2) 💬(1)
似乎问题很多都已经不属于接口测试的问题了😖
2020-02-19 - smallfish 👍(1) 💬(1)
一个很有趣的观点,json、python和java的学习成本对比,其实我想表达一个观点,对于测试人员,java优势比python大,我的观点如下:1.java市场占有率太高,开发使用的大概率是java,而测试是和开发使用的同一门语言,就会有共同话语,这样开发就会更好沟通,提高测试效率;2.所谓【人生苦短,我用python】,但其实现在IDE工具很强大,只需要负责关系逻辑代码就OK,一个玩笑话:写ython的都是使用游标卡尺量间距;3.大量的测试工具,都是使用java进行开发的,对于后期的扩展,java拥有很大的优势;4.java的学习成本并没有老师你说的那么夸张,那是对于开发人员来说的,对于测试人员,学基础就够用了,和python我的感觉是差不了太多。 老师讲的mock使用这种场景,我的实际工作中并没有遇到过,我们提测都是一次所有微服务同时提测,不会存在依赖性的服务,一个提测一个没有提测。但实际工作中mock服务还是很有用途的,我是这么使用的,是在开发开完接口设计评审和输出设计文档后,mock出接口,编写测试用例时,可以进行模拟调用,测试一下自己的用例是否OK。但老师讲的这个也是一个思路,可能有些公司会遇到这样的问题吧。
2020-03-22 - random0 👍(0) 💬(1)
请问老师,如果已经通过mock服务测试完成,此时其他服务也已经正常,这时仍需要按正常流程回归一遍吧?
2021-03-03 - 捷后愚生 👍(0) 💬(1)
本文总结 为什么需要mock? a服务--b服务--c服务,需要测试a服务,但是b服务或者c服务有问题,不能返回报文或者不能返回正确的报文 a系统--b系统--c系统,需要测试a系统,但是b系统或者c系统有问题,不能返回报文或者不能返回正确的报文 所以需要一个工具模拟返回的报文,这可以保证a能顺利的测试,这就是mock服务或者挡板工具。 怎么选取mock工具? 1.学习成本低、上手快并且完全适合你自己技术栈的 Mock 框架 2.Mock 服务容易修改和维护 3.处理速度快 4.Mock 服务要能轻量化启动,并且容易销毁 除了老师提到的mock工具,其实fiddler也是可以做mock测试的,老师怎么看?
2020-11-28 - 夜歌 👍(0) 💬(2)
老师您好,没做过微服务,所以有个想问的问题:微服务接口是怎么测试的?我想测试接口b,但接口b依赖接口a,但这是接口b在服务端调用的接口a,我该怎么mock接口a?难道说测微服务都是把被测接口的代码部署到自己本地运行,然后mock、改调用,再测试的?没做过,所以猜想是不是这样,要不感觉够不到让接口b改为走mock接口。
2020-04-24 - 西 👍(0) 💬(1)
Mock服务,阻隔关联性,可做一个通用“替身”,自定义返回值。
2020-03-27 - 明 👍(0) 💬(1)
实现分成自动化测试就要靠mock服务框架了,是吗
2020-03-23 - 技术修行者 👍(0) 💬(1)
在有mock的情况下,一般会有一个特殊的环境或者特别的部署来执行测试。在微服务架构下,可能会有很多的微服务,微服务之间的调用链会很长,在创建mock的时候需要注意:1. 哪些服务需要做mock处理,2. 服务调用时,需要哪些服务调用会调用mock服务。这两部分,都需要开发团队介入,和测试团队一起完成。在这种情况下,基于不同的测试场景,一个微服务可能会部署多个版本,对于服务网关的配置也会是一个挑战。
2020-02-26 - Tomorrow 👍(0) 💬(1)
老师您好,看见有同仁也在请教mock服务器定向访问的问题,关于这块我也想多请教一下~之前做过一个跟三方合作项目的自动化,在自动化的时候将三方接口都给mock掉了,在运维的帮助下,搭建了一个mock服务器,当时运维就给了我几个根路由,我只用在接口调用的时候带上相应的根路由+path,发出的请求就会自动发送到mock服务器上,而不去调用三方的接口。请问为啥在未改动相应开发代码的前提下,可以实现请求的转移的呢?或者有无相应的资料可以推荐我自行去查阅,感谢🙏
2020-02-23