01.微服务为什么需要契约测试

首先我介绍一下公司的情况。我们使用的是微服务架构,每个部分会负责其中的几个微服务的研发和维护。我所在的部门维护公司的支付服务(billing),这个服务需要依赖其他部门的几个服务。

当用户需要支付一笔订单时,会调用 billing 服务,同时携带很多参数,为了方便,我们先只考虑核心的两个参数:用户 id 和支付金额。

当 billing 接收到用户请求时,会调用其他的依赖服务,用户服务(user)是其中的一个。我们需要查询该用户是否有足够多的余额可以支付订单。

def pay(uid, amount):  """付款"""  user_host = app.config.get("user_server_host")  user_server = f'{user_host}/user/{uid}/pay'  resp = httpx.get(user_server)  user = resp.json()  if user.get('balance') < amount:      return {"msg": "not enough money"}  return {"msg": "success", "amount": amount, "uid": uid}

user 被很多服务调用,而 billing 主要想消费它的 balance 字段校验余额是否足够。user 判断如果是 billing 请求的 /user/ 接口,则把余额给它。其他的消费者请求时返回的数据不太一样。

def user_info(uid, src):  """用户信息"""
if src == 'pay':      return {"id": uid,"balance": 8000}
elif src == 'manage':      return { "id": uid, "age": 19}
...

某一天用户直接反馈支付出错,但是我们最近都没有对 billing 服务进行任何的修改,显然,这个突发情况可能不是由我们部门引起的。

我们只能紧急的去线上排查问题,通过一系列分析,终于发现是 user 服务返回的余额字段由 balance 改成了 user_balance,其他没有发生任何变化。user 服务修改了代码,却没有通知我们。

数据结构的轻微变化导致我们根本拿不到 balance 字段,所以我们的支付服务无法继续。


于是我只能立马给 user 部门发邮件。在微服务的维护过程中,最简单有效的测试工具是邮件。因为一个服务往往依赖于其他多个服务,又被另外的服务依赖,导致最后的数据流向变得跟蜘蛛网一样。可是当涉及到跨部门协作的时候,沟通起来也并不那么容易。

我们来看一下这么简单的一处改动会涉及到的流程。首先,user 服务收到需求更改,修改代码之后,user 的测试人员会对自己服务进行组件测试,但是 billing 调用 user 的集成测试他们没有办法测,因为他们根本不知道 billing 是怎么消费它提供的数据的。

user 决定通知所有的下游服务,凡是使用了我这个服务,这个接口的,通通发一遍邮件,麻烦你们各自部门测试一下你们的业务,我的 /user/ 接口进行了修改,可能会引发你们的服务异常。其他部门收到通知以后,再针对性的测试这个接口。

这些下游部门会因为一处小改动,浪费非常多的时间和资源。为什么呢?billing 服务是受到修改影响的,所以对我们来说是必要的。但是其他的服务虽然调用了 /user/ 这个接口,却并没有消费 balance 这个字段,balance 字段的改动对他们不会产生任何影响。但是他们在测试之前是不知道会不会有影响的,是不是浪费时间呢?

还有一个问题,billing 部门难道不可以建立对 user 服务的集成测试吗?当然可以,实际上我们有专门 mock user 服务的测试,也有对 user 的集成测试,偶尔也会进行手工测试。

但是 mock 测试、集成测试和手工测试这些测试手段都不能及时发现问题。

首先我们看看 mock 测试。mock 服务器是由我们自己掌控的,他的结果是不可信赖的,并不能完全代替真实服务,我们之所以用 mock,是因为快、可控、稳定性高,而且能做到环境隔离。

当真实的 user 服务发生变动时,mock 的数据格式并不能及时更新,所以这些测试用例并不会爆红,而是继续通过。

而集成测试和手工测试都有相同的缺陷,他们太慢了。billing 部门的整套集成测试从构建到结束需要 5 个小时,手工测试一轮就更久了。也就是说,我们至少需要 5 个小时才能发现我们依赖的服务发生了变化,而这个问题早就引发了我们的系统瘫痪长达 5 个小时之久。

契约测试是融合了 mock 测试和集成测试的综合性测试手段,他把消费者 billing 编写的测试用例形成契约文件(contract file),放到提供者 user 端去构建。

当代码修改以后,提供者 user 先针对所有的契约文件构建一次测试,如果所有的契约文件都满足,没有造成毁约现象,就可以上线了。如果有毁约,则需要和被毁约方协商解决,再上线。

附消费者和提供者示例代码,以及 mock 测试和集成测试示例代码:

billing server:

import httpx
from flask import (Flask,request)
app = Flask(__name__)
app.config.update(user_server_host='<http://localhost:5001>')@app.route('/pay/<uid>/<int:amount>')
def pay(uid, amount):  """付款"""  user_host = app.config.get("user_server_host")  user_server = f'{user_host}/user/{uid}/pay'  resp = httpx.get(user_server)  user = resp.json()  if user.get('balance') < amount:      return {"msg": "not enough money"}  return {"msg": "success","amount": amount, "uid": uid    }
if __name__ == '__main__':  app.run(port=5000, debug=True)

user_server:

from flask import (Flask, request)
app = Flask(__name__)
@app.route('/user/<uid>/<src>')
def user_info(uid, src):
"""付款"""
if src == 'pay':      return {"id": uid, "balance": 8000}
elif src == 'manage': return {"id": uid, "age": 19}
...
if __name__ == '__main__':  app.run(port=5001)

测试代码:

import unittest
from billing_server import appclass TestBilling(unittest.TestCase):  def test_mock_user_server(self):      app.config.update(user_server_host='<http://localhost:5002>')      with app.test_client() as client:          resp = client.get('/pay/1/100')          assert b"amount" in resp.data  def test_real_user_server(self):      app.config.update(user_server_host='<http://localhost:5001>')      with app.test_client() as client:         resp = client.get('/pay/1/100')          assert b"amount" in resp.data
if __name__ == '__main__':  unittest.main()

真实的 user 服务更新后,bill 服务端的测试用例如果 mock 了依赖的 user 服务,测试用例会继续通过,因为 mock 服务是不可信赖的。

如果使用集成测试方法,直接调用远端的服务,不可避免的会造成测试运行很慢,如果整套测试运行需要 2 个小时,则会造成用户无法使用 2 个小时后,才能发现问题。

最后: 可以关注公众号:伤心的辣条 ! 进去有许多资料共享!资料都是面试时面试官必问的知识点,也包括了很多测试行业常见知识,其中包括了有基础知识、Linux必备、Shell、互联网程序原理、Mysql数据库、抓包工具专题、接口测试工具、测试进阶-Python编程、Web自动化测试、APP自动化测试、接口自动化测试、测试高级持续集成、测试架构开发测试框架、性能测试、安全测试等。

如果我的博客对你有帮助、如果你喜欢我的博客内容,请 “点赞” “评论” “收藏” 一键三连哦!推荐软件测试交流学习群:914172719 里面会分享一些资深架构师录制的视频录像


好文推荐

转行面试,跳槽面试,软件测试人员都必须知道的这几种面试技巧!

面试经:一线城市搬砖!又面软件测试岗,5000就知足了…

面试官:工作三年,还来面初级测试?恐怕你的软件测试工程师的头衔要加双引号…

什么样的人适合从事软件测试工作?

那个准点下班的人,比我先升职了…

测试岗反复跳槽,跳着跳着就跳没了…

看本质:微服务为什么需要契约测试?相关推荐

  1. 微服务架构实战第十节 微服务的模拟组件测试和契约服务测试

    32 测试方案:如何正确理解针对微服务的测试解决方案? 作为整个课程最后一部分内容,我们将讨论微服务架构中的测试解决方案.对于微服务而言,测试是一个难点,也是经常被忽略的一套技术体系.当系统中存在多个 ...

  2. 微服务模式下API测试

    来自茹炳晟 session和cookie的区别 如果后端工程师使用 session 记录使用者登入信息,那么后端通常会传一个 session ID 给前端.之后,前端在发给后端的 requests 的 ...

  3. 一张图看懂微服务架构路线

    目录 一张图看懂微服务架构路线 我为什么选择微服务架构? 微服务架构路线 基本思路 Docker 容器编排 Docker 容器管理 API网关 负载均衡 服务发现 事件总线 日志记录 监控和警报 分布 ...

  4. 利用Diferencia和Java微服务进行分接比较测试

    本文要点 在微服务体系结构中,许多服务可能同时在(相对)独立地演化,而且通常非常迅速.要获得这种架构风格的全部价值,服务必须能够独立发布. 通常很难验证新服务(或服务的新版本)没有对当前的应用程序造成 ...

  5. sql server配置管理器在哪里看ip_微服务管理平台nacos虚拟ip负载均衡集群模式搭建...

    一.Nacos简介 Nacos是用于微服务管理的平台,其核心功能是服务注册与发现.服务配置管理. Nacos作为服务注册发现组件,可以替换Spring Cloud应用中传统的服务注册于发现组件,如:E ...

  6. 测试一年多,上线就崩溃!微服务到底应该怎么测试?

    简介:只有了解风险,才能及时应对,保障服务高可用. 不久前,也就是11月16日,澳大利亚交易所(Australian Securities Exchange, ASX)上线了一个新的交易系统,但因为出 ...

  7. 从 Spring Cloud 看一个微服务框架的五脏六腑

    Spring Cloud 是一个基于 Spring Boot 实现的微服务框架,它包含了实现微服务架构所需的各种组件. 注:Spring Boot 简单理解就是简化 Spring 项目的搭建.配置.组 ...

  8. 从 Spring Cloud 看一个微服务框架的「五脏六腑」

    点击上方"Java学习之道",选择"关注"公众号 每天早晨,干货准时奉上! 本文作者:酷家乐前端团队 来自:webfe.kujiale.com/spring-c ...

  9. 从 Spring Cloud 看一个微服务框架的「五脏六腑]

    https://webfe.kujiale.com/spring-could-heart/ Spring Cloud 是一个基于 Spring Boot 实现的微服务框架,它包含了实现微服务架构所需的 ...

最新文章

  1. 最小生成树学习-Kruskal算法
  2. 技术干货 | 为高音质保驾护航 - 通信中的回声消除
  3. Python——基本统计值计算
  4. 关于QT中奇数个汉字出现newline in constant的错误
  5. 9位工作流业内专家联袂推荐
  6. Python3 字符串操作
  7. Linux命令行下播放音乐SOX
  8. CAM350学习进行时
  9. msdia140.dll 已加载,但对DllRegisterServer 的调用失败, 错误代码: 0x80070005
  10. Wallpaper Engine软件——html做为壁纸
  11. OpenGL学习笔记一
  12. 如何将多个txt文件合并成一个文本?
  13. sqlmap-sql注入检测
  14. java数组的四种拷贝方式
  15. 第三讲 AHRS姿态解算
  16. Memcached的LRU策略
  17. DOSBOX + MASM
  18. atoi和itoa的模拟实现
  19. 英文学习20180321
  20. 代理服务器、虚拟专用网络、网关

热门文章

  1. 宝岛探险1(BFS)
  2. c语言 判断一个图是否全连通_【连载】(判断执行语句)乐创DIY C语言讲义——3.8节(2)...
  3. buuct 假如给我三天光明 misc_假如给我三天光明读后感
  4. 代码chaid_R或Python中的CHAID决策树
  5. sticky-footer布局
  6. kill 的常用信号
  7. Silverlight 动画性能
  8. 把emacs的插件和配置文件备份到github上去了。
  9. 使用alias简化命令输入
  10. asp.net 下载大文件