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 工具,那么你在工作中,有没有遇见过被测系统因为外部依赖,而不得不阻塞项目进度的时候呢?你又是怎么解决的呢?
我是陈磊,欢迎你在留言区留言分享你的观点,如果这篇文章让你有新的启发,也欢迎你把文章分享给你的朋友,我们一起沟通探讨。
文章作者 anonymous
上次更新 2024-04-26