简易版的接口流量回放工具Parrot

时间:2024-03-04 09:34:46

写在前面

软件测试在很多时候都是流量回放的模式,这里的“流量回放”的定义比较宽泛,通常包含如下三个步骤:

  • 录制:定义/获取执行步骤和预期结果
  • 回放:执行预定步骤,收集实际结果
  • 验证:对比预期结果和实际结果,判定测试结果

软件测试的一般定义:按照既定的步骤,运行系统或应用程序,得到实际结果,和预期结果进行比较。

本文主要介绍http接口的流量回放方式,目的是取代常规的接口自动化,暂无法应用于性能测试。

项目开源地址:https://github.com/idle-man/Parrot (代码比较粗糙,欢迎指教)


步骤一:录制

前面有提到,录制主要是定义/获取执行步骤和预期结果,就接口测试而言,需要拿到的信息有:

  • 被调用的接口及传参
  • 接口间的调用顺序及依赖关系
  • 各接口的预期响应

综合业界常见的测试方式和已有的流量回放手段,通常有以下几种录制方式:

  • 生产环境引流:采用TCPDump、TCPCopy或网关/路由层进行转发;该方式需要一定的部署权限
  • 离线日志解析:基于标准化的日志解析得到完整信息;该方式对日志规范化有较高的统一的要求
  • 测试工具抓包:如Fiddler、Charles,测试及回归过程中均可积累;可做标准化解析,数据量级相对较小
  • 接口文档定义:基于严格的标准化接口文档;该方式条件相对苛刻很多,且接口文档需要有可执行的示例

本文所选取的方式为:基于测试工具抓包,测试数据真实、由专业的测试人员定制、容易获取和更新(替换)、是常规的自动化回归的思路

需要解决的问题:

  • 批量抓包导出的请求list中,可能包含部分不需要被回放的请求,如何自动剔除
  • Fiddler导出的txt和Charles导出的trace,文本格式标准化但有差异,需要分别解析

Parrot的解法:拿来抓包(Fiddler .txt or Charles .trace),写个配置(yaml record部分),执行recorder.py搞定

  • 配置支持select、ignore模式,可根据host(全部匹配)、url(部分匹配)进行筛选,默认录制全部
  • parser会根据传入文件的后缀自动判定为Fiddler or Charles抓包,分别进行解析,生成标准化的ini文件

步骤二:回放

有了录制阶段拿到的标准化数据后,对于单个请求的回放不难做,本项目主要是简单封装了python requests模块来实现。

做过实际项目接口自动化的同学,通常面临的场景远不是单独按照既定参数调用一个接口那么简单,一般都会面临如下问题:

  • 部分参数需要实时变动,例如请求传参中可能包含“日期”,抓包拿到的日期等回放的时候可能就失效了
  • 部分参数需要基于依赖关系获取,例如A接口Response中的某个key需要作为B接口的实时传参,单独使用抓包的数据是无效的
  • 部分接口需要拿到特定结果后才能继续执行,抓包时可能A接口较快拿到了数据,回放时可能就不是这样了,例如刷票行为
  • 多个接口的调用顺序需要保证,这个相对容易处理,因为抓包中基本都会包含各接口的调用时间,Parrot可按照该次序和当时的时间间隔执行

Parrot的解法:写个配置(yaml replay部分),执行replayer.py搞定

  • 配置支持replace模式,可进行纯静态替换(example => t.example),可进行动态替换(2019/05/01 => {{Today()}}
  • 依赖关系可以通过配置中的store和replace模式配合,例如:store配置apiA::resp::sign(执行到apiA接口时会自动将该值计入内存),replace配置apiB::{sign} => {get::resp::sign}(执行apiB接口之前会实时替换该传参)
  • 配置中的wait模式,可使用time方式来配置apiA: 1(执行完apiA接口后,额外等待1秒),也可使用rule方式配置apiA::data.code: \'==200\'(执行完apiA接口检查Response是否符合,不符合则重复执行A,直到符合或超出最大重试次数-global_conf中定义)
  • 一般项目的很多接口有前后执行顺序要求或依赖关系,Parrot默认的回放方式为串行且遵循录制的时间间隔;若无依赖关系,想并发执行的话,可自行修改config.py中的global_conf

Parrot回放完成后,同样会生成标准化的ini数据文件,供后续的对比验证使用。


步骤三:验证

目前大不多数项目http接口的Response采用的都是json格式,Parrot的验证逻辑即是对录制的Response和回放执行拿到的Response进行json diff。

如果只是做全量的文本比较,直接采用两个字符串进行相等匹配即可,但实际项目中极少如此,一般会面临如下问题:

  • Response中存在部分不需要对比且经常变化的key,例如:timestamp、token、sign等,需要在对比时进行剔除
  • 部分接口的Response可能只需要比对关键的一些key就够了,例如:status、code,其余信息无需关注
  • 部分接口的Response的某些key的值通过对比不足以验证,希望进行额外的规则验证,例如:某值>0,某list非空
  • 部分接口的Response的某些key的值是list或字典格式,作为通用工具在未知层级的情况下,如何写个配置就能对比了

Parrot的解法:写个配置(yaml check部分),执行checker.py搞定

  • 配置支持select、ignore模式,可选定/忽略Response中的部分key,可针对特定接口(部分匹配)或默认的全部,多层级写法:data.key1、data.key1.key2[0].key3
  • 配置支持rule模式,可配置验证规则,可针对特定接口(部分匹配)或默认的全部,例如:apiA::data.status: \'==200\'、apiB::data.list: \'!= []\'
  • 本项目的json diff的主要思路是,分别将录制的预期Response和回放的实际Response json分别解析为层级深度为1的键值对,如:data.key1.key2[0].key3 = 1,如此可方便使用者进行更好的配置

Parrot的对比验证会输出html report,其中可见如下信息:

  • 汇总信息,总条数、成功多少、失败多少
  • 每条请求的status(http状态码)对比结果,duration(请求耗时)对比结果,response(响应信息)对比结果,request的详情

以上

文字功底所限,可能尚有很多细节未描述清楚,欢迎留言交流;

Parrot项目已开源,代码粗糙,欢迎大神提改进建议。

最后,再次附一下地址:https://github.com/idle-man/Parrot 

愿意的话,不妨给个Star~