博客
关于我
apiAutoTest:基于mitmproxy实现接口录制
阅读量:729 次
发布时间:2019-03-22

本文共 5446 字,大约阅读时间需要 18 分钟。

目录

apiAutoTest

先软文介绍下:apiAutoTest是个人和众多测试同行参与(提供新的需求)的一个接口测试工具项目.

Gitee:

Github:

目前功能

  • [x] 测试前后数据库备份操作,个人理解算数据清洗
  • [x] 各接口之间的测试数据依赖
  • [x] 自定义扩展函数定义,解决部分加密算法
  • [x] 后置sql,结果用于依赖或者断言(select 语句只能查出第一条)1. * *
  • [x] 实际结果可动态提取,与预期结果绝对==
  • [x] 可选用例失败重跑机制
  • [x] 基于mitmproxy录制接口生成用例文件

重大更新(个人认为)

在之前的一篇自定义函数简单实现方式时,有提醒到语法可能出现冲突,所以在前两天更新时已经统一了语法${}

无论是使用依赖参数还是自定义方法都使用${}, 为了避免每次使用其他接口返回提取jsonpath表达式在用例中的冗余(或许也提高了些性能,之前版本是会保存整个响应内容的),用例中增加了提取参数来实现形式如下

{    // key -> id 为其他接口使用时的参数变量 用法 ${id}    "id": "$.data.id" // $.data.id 实则为jsonpath表达式 从当前响应中提取id值}

相关使用文档: 可到源码Readme.md 文件中前往 在线文档查看

本次更新

**本次更新内容使用演示视频: **

契机

有同志,希望有个录制功能来减少手写参数的时间

根本

基于mitmproxy, 使用其提供的扩展API, 通过mitmproxy 实现代理之后捕获到HTTP/HTTPS请求,并把请求已追加的形式添加到excel中,当录制完成务必使用ctrl + c 关闭录制,将生成一个完成的用例数据文件

可指定录制包含请求地址的接口

如何录制

  1. 前置条件:

  2. 打开本机代理

  3. 修改tools\recording.py中配置抓包请求地址, 用例生成路径

  4. apiAutoTest根目录下执行

    mitmweb -s tools\recording.py

  5. 正常去使用就行了,当不需要录制的时候 在上面这个窗口Ctrl + C停止录制,然后关闭本机代理

录制的用例

因为默认录制的url是完整的url,所以如果直接用这个文件,请把config/config.yaml中的serve dev 基准地址换成"", 因为条件有限没法覆盖测试很多内容这快功能可能会有Bug, 目前个人测试了Graphql规范接口的录制,RestFul规范接口录制, 不排除其他的无法完整的生成用例文件

需要注意Excel 单元格字符数限制问题, Graphql规范接口非常容易出现不可写入的情况, 单从业务接口来说应该不容易出现此类问题

执行录制的用例

config/config.yaml修改基准地址dev"",指定使用录制的用例文件

server:  # 本地接口服务  test: http://127.0.0.1:8888/  # https://space.bilibili.com/283273603 演示项目后端服务来自#  dev: http://www.ysqorz.top:8888/api/private/v1/  dev: ''# 基准的请求头信息request_headers: {}file_path:  test_case: data/case_data1.xls	# 指定使用那个用例,这里使用了录制的用例  report: report/  log: log/run{time}.log....

执行结果

实现源码

#!/usr/bin/env/ python3# -*- coding:utf-8 -*-"""@Project: apiAutoTest@File  :recording.py@Author:zy7y@Date  :2021/5/21 22:07@Desc  : 录制接口,生成用例文件基于mitmproxy实现参考资料:https://blog.wolfogre.com/posts/usage-of-mitmproxy/https://www.cnblogs.com/liuwanqiu/p/10697373.html"""import jsonimport mitmproxy.httpimport xlwt# 上传文件接口不能录入文件参数 , excel单元格限制: Exception: String longer than 32767 charactersfrom mitmproxy import ctxclass Counter:    def __init__(self, filter_url: str, filename: str = "data/case_data1.xls"):        """        基于mitmproxy抓包生成用例数据        :param filter_url: 需要过滤的url        :param filename: 生成用例文件路径        """        self.url = filter_url        self.excel_row = [            '编号',            '用例标题',            '请求头',            '接口地址',            '是否执行',            '请求方式',            '入参关键字',            '上传文件',            '请求数据',            '提取参数',            '后置sql',            '预期结果']        self.cases = [self.excel_row]        self.counter = 1        self.file = filename    def response(self, flow: mitmproxy.http.HTTPFlow):        """        mitmproxy抓包处理响应,在这里汇总需要数据        :param flow:        :return:        """        if self.url in flow.request.url:            # 编号            number = "C" + str(self.counter)            # 标题            title = "mitmproxy录制接口" + str(self.counter)            try:                token = flow.request.headers["Authorization"]            except KeyError:                token = ''            header = json.dumps({"Authorization": token})            data = flow.request.text            # 请求地址,config.yaml 里面基准环境地址 写 空字符串            method = flow.request.method.lower()            url = flow.request.url            try:                content_type = flow.request.headers['Content-Type']            except KeyError:                content_type = ''            if 'form' in content_type:                data_type = "data"            elif 'json' in content_type:                data_type = 'json'            else:                data_type = 'params'                if '?' in url:                    data = url.split('?')[1]            data = self.handle_form(data)            # 预期结果            expect = json.dumps(                {".": json.loads(flow.response.text)}, ensure_ascii=False)            # 日志            ctx.log.info(url)            ctx.log.info(header)            ctx.log.info(content_type)            ctx.log.info(method)            ctx.log.info(data)            ctx.log.info(flow.response.text)            case = [                number,                title,                header,                url.split('?')[0],                "是",                method,                data_type,                "",                data,                "",                "",                expect]            self.cases.append(case)            self.counter += 1            # 文件末尾追加            self.excel_cases()    def excel_cases(self):        """        对二维列表cases进行循环并将内容写入单元格中        :return:        """        workbook = xlwt.Workbook()        worksheet = workbook.add_sheet('用例数据')        for x in range(len(self.cases)):            for y in range(len(self.cases[x])):                worksheet.write(x, y, self.cases[x][y])        try:            workbook.save(self.file)        except Exception as e:            print(e)    def handle_form(self, data: str):        """        处理 Content-Type:	application/x-www-form-urlencoded        默认生成的数据 username=admin&password=123456        :param data: 获取的data 类似这样  username=admin&password=123456        :return:        """        data_dict = {}        if data.startswith('{') and data.endswith('}'):            return data        try:            for i in data.split('&'):                data_dict[i.split('=')[0]] = i.split('=')[1]            return json.dumps(data_dict)        except IndexError:            return ''addons = [    Counter("http://www.ysqorz.top:8888/api/private/v1/")]"""mitmweb -s tools\recording.py 启动ctrl + C 停止 并生成完整用例"""

参考资料

你可能感兴趣的文章
初次安装webpack之后,提示安装webpack-cli
查看>>
使用FileZilla,FTP登录出现错误:FileZilla状态: 不安全的服务器,不支持 FTP over TLS
查看>>
Hbase压力测试
查看>>
C#中的类、方法和属性
查看>>
Python爬虫训练:爬取酷燃网视频数据
查看>>
Python数据分析入门(十九):绘制散点图
查看>>
Callable中call方法和Runnable中run方法的区别
查看>>
Linux yum提示Loaded plugins错误的解决方法
查看>>
Netty的体系结构及使用
查看>>
xshell解决文本粘贴格式错误
查看>>
什么是证券型代币?
查看>>
Android中获取并设置屏幕亮度
查看>>
MVVM_Template
查看>>
网络+图片加载框架(英文版)
查看>>
Python imageio方法示例
查看>>
Possible missing firmware
查看>>
JAVA BigInteger和BigDecimal类常用方式
查看>>
深度学习框架 各种模型下载集合 -- models list
查看>>
six.move 的作用
查看>>
错误:'BasicLSTMCell' object has no attribute '_kernel'
查看>>