zoukankan      html  css  js  c++  java
  • apiAutoTest:基于mitmproxy实现接口录制

    apiAutoTest

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

    Gitee: https://gitee.com/zy7y/apiAutoTest

    Github: https://github.com/zy7y/apiAutoTest

    目前功能

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

    重大更新(个人认为)

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

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

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

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

    本次更新

    **本次更新内容使用演示视频: 点击访问B站 **

    契机

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

    根本

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

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

    如何录制

    1. 前置条件: https://www.cnblogs.com/zy7y/p/14798151.html

    2. 打开本机代理

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

    4. apiAutoTest根目录下执行

      mitmweb -s tools
      ecording.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 json
    
    import mitmproxy.http
    import xlwt
    
    # 上传文件接口不能录入文件参数 , excel单元格限制: Exception: String longer than 32767 characters
    from mitmproxy import ctx
    
    
    class 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
    ecording.py 启动
    ctrl + C 停止 并生成完整用例
    """
    
    

    参考资料

    https://docs.mitmproxy.org/stable/
    https://blog.wolfogre.com/posts/usage-of-mitmproxy/
    https://www.cnblogs.com/liuwanqiu/p/10697373.html

    作者:zy7y
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文链接,否则保留追究法律责任的权利。
  • 相关阅读:
    Idea创建Hibernate bean类和.xml文件
    Hibernate 一对多,多对多,多对一检索策略
    Hibernate 性能优化一对一关联映射
    HashSet 与TreeSet和LinkedHashSet的区别
    使用LinkedList模拟一个堆栈或者队列数据结构
    设计模式------工厂模式和抽象工厂模式
    悟透JavaScript
    JS产生随机数的几个用法!
    防盗链
    nigin配置安全:三个案例看Nginx配置安全(转)
  • 原文地址:https://www.cnblogs.com/zy7y/p/14799659.html
Copyright © 2011-2022 走看看