pytest+request+yaml+allure搭建低编码调试门槛的接口自动化框架

接口自动化非常简单,大致分为以下几步:

  1. 准备入参
  2. 调用接口
  3. 拿到2中response,继续组装入参,调用下一个接口
  4. 重复步骤3
  5. 校验结果是否符合预期

一个优秀接口自动化框架的特点:

  1. 【编码门槛低】,又【能让新手学到技术】
  2. 【低调试门槛】
  3. 【优秀的可读性】
  4. 【很好的可维护性】
  5. 能很【方便多人协同】,以便【自动化代码能不断积累】形成规模效应,且【自动化case覆盖率可量化】
  6. 有失败重试机制,克服环境的不稳定
  7. 【清晰的测试报告】
  8. 能【低成本且灵活地触发】(指定环境、指定时间、指定范围)

综上,我们需要搭建一个同时满足以上特点的轻量级接口自动化框架

首先,管理接口

    达成:【编码门槛低】【方便多人协同】【优秀的可读性】【很好的可维护性】

  • 被测接口单独管理,目录结构和和开发使用的yapi保持一致,统一放置在仓库下,“接口管理”目录中;如下:

        

  • 每个接口使用yaml文件管理,如下,且使用中文命名增加可读性。
  • method: post
    host: "${host}"
    url: /copy/trading/v1/follower/edit
    headers:
      {token: "799b72f3f94973f5a54a54204eac96a1aa94cd5d365245929098e921b2ddc154"}
    body:
      {
        "followType": 1,
        "traderUid": 102806,
        "margin": "100",
        "followAmount": "1000",
        "stopLossRation": null,
        "positionMode": 1,
        "positionModeConfig": 0,
        "leverModeConfig": 0,
        "leverage": 1,
        "marginModeConfig": 0,
        "marginMode": 0,
        "positionRisk": null,
        "slippage": "0.006",
      }
    response:
      code: # 响应码(0: 成功)
      msg: #  返回描述(code为0时,返回:成功)
      data: # 响应数据
    
    
    
  • 调用接口时,默认用yaml文件中的参数(剔除response,response只做示例用),如需替换,会在调用过程中,标记具体需要替换的key value即可,如下,方便快捷
  • r.invoke_api("更新用户配置.yaml", 
                     headers={"token": 'trader_son_token'}, 
                     body={"positionMode": 1})

其次,编写代码,调用接口

        达成:【编码门槛低】【优秀的可读性】【低调试门槛】【清晰的测试报告】

  • 封装requests包,使用requests.session(),根据上述接口文档,调用get或post方法,且使用allure将入参和返回都放置到测试报告里面,且此块代码无需实际接口测试同学关注。【能让新手学到技术】
  • class RequestUtil:
        def invoke_api(self, api, headers=None, body=None):
            """
            解析需要调用的接口,并且进行参数替换,基础断言等
            :param api: 需要调用的接口
            :param header: 需要传入的请求头
            :param body: 需要传入的请求体
            :return: 返回接口响应的结果
            """
            api_name = api
            param = YamlUtil.parse_api(api_name)
            if headers is not None:
                for key in headers:
                    param['headers'][key] = headers[key]
            if body is not None:
                for key in body:
                    param['body'][key] = body[key]
            params = self.replace_value(param)
            with allure.step(f"步骤:{api_name}"):
                response = self.send_request(params)
                if self.run_mode == 1:
                    print("")
                    print(f"调用接口:{api_name}")
                    print(params)
                    print(response)
                    return response
                else:
                    return response
    
        def send_request(self, *args):
            """
            发起接口请求
            :param args: 接口请求的参数
            :return: 接口返回的数据
            """
            method = args[0]['method'].lower()
            ip = self.replace_value(args[0]['host'])
            url = ip + args[0]['url']
            headers = args[0]['headers']
            body = args[0]['body']
            data = body
            if method == "get":
                res = self.sess.get(url, params=data, headers=headers)
            elif method == "post":
                if 'files' in args:
                    files = args[0]['files']
                    file_path = os.path.join(YamlUtil.data_file_path, files)
                    with open(file_path, 'rb') as file:
                        files = {'file': ('image.jpg', file)}
                        res = self.sess.post(url, files=files, headers=headers)
                res = self.sess.post(url, json=data, headers=headers)
            else:
                print("请求方式错误")
            try:
                res = res.json()
                with allure.step(f"URL:{url}"):
                    ...
                with allure.step(f"Method:{method}"):
                    ...
                with allure.step(f"Headers:{json.dumps(headers)}"):
                    ...
                with allure.step(f"Body:{json.dumps(data, ensure_ascii=False)}"):
                    ...
                with allure.step(f"Response:{json.dumps(res, ensure_ascii=False)}"):
                    return res
            except Exception as e:
                print("该接口返回的结果不是json数据?")
                res = res.text
                with allure.step(f"URL:{url}"):
                    ...
                with allure.step(f"Method:{method}"):
                    ...
                with allure.step(f"Headers:{json.dumps(headers)}"):
                    ...
                with allure.step(f"Body:{json.dumps(data, ensure_ascii=False)}"):
                    ...
                with allure.step(f"Response:{json.dumps(res, ensure_ascii=False)}"):
                    return res
  • 通过pytest的fixture装饰器,将RequestUtil注入进去每个测试用例里面去
  • @pytest.fixture(scope="session")
    def r():
        return RequestUtil()
    

  • 测试编码同学只需要使用方法,即可完成接口调用和response获取
  • 接口:百度.yaml
  • method: post
    host: https://ug.baidu.com
    url: /mcp/pc/pcsearch
    headers:
      {}
    body:
      {
        "errno": 0,
        "errmsg": "ok",
        "data": {
          "log_id": "1652589279",
          "action_rule": {
            "pos_1": [],
            "pos_2": [],
            "pos_3": []
          }
        }
      }

    事实上,测试代码只有这么点:【编码门槛低】只需要能看懂接口文档,和http的基本知识,以及知道Python的字典和assert,便可轻松完成编码

    def test_百度(r):#其中 r为fixture装饰器,通过conftest.py注入进去
        resdic= r.invoke_api("百度.yaml",
                                 headers={},
                                 body={})
    
    assert resdic[errmsg] == "ok"
    assert resdic[data][log_id] == "3277317105"
    
    
  • 不管成功失败,测试报告都会打出每一次请求的入参和返回,如下图,都不用在IDE里面调试。【低调试门槛】【清晰的测试报告】
  • 另外,数据驱动的case,用如下的方式编写
  • import pytest
    
    
    @pytest.mark.parametrize('emails,password,assert1', [
        ('11111@qq.com', 'Tset111', 'success'),
        ('22222@qq.com', 'Tset222', 'fail'),
        ('33333@qq.com', 'Tset333', 'error'),
    ])
    def test_注册用户(r, emails, password, assert1):
        response_info = r.invoke_api('注册用户.yaml', body={"emails": emails, "password": password})
        assert response_info['data'] == assert1
    

再次,测试用例的组织和结构

        达成:【方便多人协同】【自动化代码能不断积累】【自动化case覆盖率可量化】

  1. 手工用例遵循实际的业务模块树形结构,做较精细的模块拆分
  2. 末级手工用例使用xmind管理,作为用例树的末级,标题上按如下打标:哪些场景需要被覆盖(目标)?已被覆盖(进度)?功能覆盖率多少(量化)?哪些地方还需要验证前端?一目了然,工时评估更透明
    • 是否需要自动化
    • 是否已被自动化
    • 是否还需要验证前端
  3. 自动化测试用例目录,和手工用例目录保持一致,
  4. 每个自动化case一个.py文件,自动化测试用例名与手工测试名保持一致。如此保障一一对应
  5. 如若涉及一对多:
    1. 如果涉及单接口的,自动化测试用例名与手工用例末级目录一致;
    2. 如果涉及数据驱动的,自动化测试用例名与手工用例末级目录一致;
  6. 通过以上措施,很方便计算出自动化case覆盖率(分子为已完成自动化case,分母为达标为:需要自动化的case数)

再次,通过配置文件,和参数化运行

        达成【低成本且灵活地触发】

  • yaml方式的配置文件
  • run_mode: 2 # 1调试模式/2正式模式
    run_env: one  # 运行脚本环境
    
    test:
      host: http://api.test.xyz
      db:
        host:
        port:
        user:
        password:
      redis:
        host:
        password:
    
    
    testa:
      host: http://api.testa.xyz
      db:
        host:
        port:
        user:
        password:
      redis:
        host:
        password:
    
    # 环境
    one:
      # 接口地址
      host: 
      # b连接信息
      db:
        host:
        port:
        user:
        password:
      # redis连接信息
      redis:
        host:
        password:
    
    
  • main函数运行的时候,拿到具体运行的环境信息,读取对应的配置
  • import os
    import pytest
    from pytest_jsonreport.plugin import JSONReport
    import argparse
    
    from Core.parse_yaml import YamlUtil
    from Core.robot_utils import Robot
    
    
    def main(env):
        YamlUtil.clear_env_yaml()
        if env.env is not None:
            e = env.env
            data = {"env": e}
            YamlUtil.write_env_yaml(data)
    
        current_path = os.path.dirname(os.path.abspath(__file__))
        json_report_path = os.path.join(current_path, 'report/json')
        html_report_path = os.path.join(current_path, 'report/html')
        plugin = JSONReport()
    
        pytest.main(["自动化用例/百度搜索.py", '--alluredir=%s' % json_report_path, '--clean-alluredir'], plugins=[plugin])
    
        # 生成allure报告
        os.system('allure generate %s -o %s --clean' % (json_report_path, html_report_path))
        os.system('allure open %s' % html_report_path)
    
    
    if __name__ == "__main__":
        parser = argparse.ArgumentParser()
        parser.add_argument("--env", help="参数为需要执行的环境")
        args = parser.parse_args()
        main(args)
  • #在testa环境运行
    python run.py --env testa

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/954016.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于Springboot + vue实现的文档管理系统

🥂(❁◡❁)您的点赞👍➕评论📝➕收藏⭐是作者创作的最大动力🤞 💖📕🎉🔥 支持我:点赞👍收藏⭐️留言📝欢迎留言讨论 🔥🔥&…

Pycharm连接远程解释器

这里写目录标题 0 前言1 给项目添加解释器2 通过SSH连接3 找到远程服务器的torch环境所对应的python路径,并设置同步映射(1)配置服务器的系统环境(2)配置服务器的conda环境 4 进入到程序入口(main.py&#…

初学stm32 --- II2C_AT24C02,向EEPROM中读写数据

目录 IIC总线协议介绍 IIC总线结构图 IIC协议时序 1. ACK(Acknowledge) 2. NACK(Not Acknowledge) IO口模拟II2C协议 发送起始信号: 发送停止信号: 检测应答信号: 发送应答信号&#x…

Excel 技巧07 - 如何计算到两个日期之间的工作日数?(★)如何排除节假日计算两个日期之间的工作日数?

本文讲了如何在Excel中计算两个日期之间的工作日数,以及如何排除节假日计算两个日期之间的工作日数。 1,如何计算到两个日期之间的工作日数? 其实就是利用 NETWORKDAYS.INTL 函数 - weekend: 1 - 星期六,星期日 2,如…

保姆级图文详解:Linux和Docker常用终端命令

文章目录 前言1、Docker 常用命令1.1、镜像管理1.2、容器管理1.3、网络管理1.4、数据卷管理1.5、监控和性能管理 2、Linux 常用命令分类2.1、文件和目录管理2.2、用户管理2.3、系统监控和性能2.4、软件包管理2.5、网络管理 前言 亲爱的家人们,技术图文创作很不容易…

从玩具到工业控制--51单片机的跨界传奇【2】

咱们在上一篇博客里面讲解了什么是单片机《单片机入门》,让大家对单片机有了初步的了解。我们今天继续讲解一些有关单片机的知识,顺便也讲解一下我们单片机用到的C语言知识。如果你对C语言还不太了解的话,可以看看博主的C语言专栏哟&#xff…

智能物流升级利器——SAIL-RK3576核心板AI边缘计算网关设计方案(一)

近年来,随着物流行业智能化和自动化水平不断提升,数据的实时处理与智能决策成为推动物流运输、仓储管理和配送优化的重要手段。传统的集中式云平台虽然具备强大计算能力,但高延迟和带宽限制往往制约了物流现场的即时响应。为此,我…

HTML拖拽功能(纯html5+JS实现)

1、HTML拖拽--单元行拖动 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><…

Jaeger UI使用、采集应用API排除特定路径

Jaeger使用 注&#xff1a; Jaeger服务端版本为&#xff1a;jaegertracing/all-in-one-1.6.0 OpenTracing版本为&#xff1a;0.33.0&#xff0c;最后一个版本&#xff0c;停留在May 06, 2019。最好升级到OpenTelemetry。 Jaeger客户端版本为&#xff1a;jaeger-client-1.3.2。…

【MySQL】简单解析一条SQL查询语句的执行过程

1. MySQL 的逻辑架构图 MySQL 架构主要分为 Server 层和存储引擎层。Server 层集成了连接器、查询缓存、分析器、优化器和执行器等核心组件&#xff0c;负责提供诸如日期、时间、数学和加密等内置函数&#xff0c;以及实现存储过程、触发器、视图等跨存储引擎的功能。存储引擎层…

如何将json字符串格式化

文章目录 如何对json字符串进行格式化显示hutool方案的示例和不足使用fastjson的方案 如何对json字符串进行格式化显示 将json字符串内容进行格式化的输出显示。本文介绍 hutool的方案和alibaba 的fastjson方案 hutool方案的示例和不足 引入依赖 <dependency><grou…

复杂 C++ 项目堆栈保留以及 eBPF 性能分析

在构建和维护复杂的 C 项目时&#xff0c;性能优化和内存管理是至关重要的。当我们面对性能瓶颈或内存泄露时&#xff0c;可以使用eBPF&#xff08;Extended Berkeley Packet Filter&#xff09;和 BCC&#xff08;BPF Compiler Collection&#xff09;工具来分析。如我们在Red…

unity学习18:unity里的 Debug.Log相关

目录 1 unity里的 Debug.log相关 2 用Debug.DrawLine 和 Debug.DrawRay画线 2.1 画线 1 unity里的 Debug.log相关 除了常用的 Debug.Log&#xff0c;还有另外2个 Debug.Log("Debug.Log"); Debug.LogWarning("Debug.LogWarning"); Debug.LogErro…

IoTDB 常见问题 QA 第三期

关于 IoTDB 的 Q & A IoTDB Q&A 第三期持续更新&#xff01;我们将定期汇总我们将定期汇总社区讨论频繁的问题&#xff0c;并展开进行详细回答&#xff0c;通过积累常见问题“小百科”&#xff0c;方便大家使用 IoTDB。 Q1&#xff1a;查询最新值 & null 数据相加方…

MySQL数据库(SQL分类)

SQL分类 分类全称解释DDLData Definition Language数据定义语言&#xff0c;用来定义数据库对象&#xff08;数据库&#xff0c;表&#xff0c;字段&#xff09;DMLData Manipulation Language数据操作语言&#xff0c;用来对数据库表中的数据进行增删改DQLData Query Languag…

Swift 趣味开发:查找拼音首字母全部相同的 4 字成语(上)

概述 Swift 语言是一门现代化、安全、强大且还算性感的语言。在去年 WWDC 24 中苹果正式推出了秃头码农们期待许久的 Swift 6.0&#xff0c;它进一步完善了 Swift 语言的语法和语义&#xff0c;并再接再厉——强化了现代化并发模型的安全性和灵活性。 这里我们不妨用 Swift 来…

C++ STL之容器介绍(vector、list、set、map)

1 STL基本概念 C有两大思想&#xff0c;面向对象和泛型编程。泛型编程指编写代码时不必指定具体的数据类型&#xff0c;而是使用模板来代替实际类型&#xff0c;这样编写的函数或类可以在之后应用于各种数据类型。而STL就是C泛型编程的一个杰出例子。STL&#xff08;Standard …

VUE3 + Ant Design Vue4 开发笔记

异常记录 [Vue warn]: Extraneous non-props attributes (options) were passed to component but could not be automatically inherited because component renders fragment or text root nodes 定位原因解决方法 错误的中文释义&#xff1a;[Vue 警告]&#xff1a;传递给…

QT跨平台应用程序开发框架(2)—— 初识QT

目录 一&#xff0c;创建helloworld 1.1 通过图形化 1.2 通过代码 1.3 通过编辑框 1.4 使用按钮 二&#xff0c;对象树 2.1 关于对象树 2.2 演示释放流程 三&#xff0c;乱码问题 3.1 为什么会有乱码问题 3.2 解决乱码问题 四&#xff0c;认识Qt坐标系 五&#xf…

【搭建JavaEE】(3)前后端交互,请求响应机制,JDBC数据库连接

前后端交互 Apache Tomat B/S目前主流。 tomat包含2部分&#xff1a; apache容器 再认识servlet 抽象出的开发模式 项目创建配置 maven javaeetomcat 忽略一些不用的文件 webapp文件夹 HiServlet 这里面出现了webinfo&#xff0c;这个别删因为这个呢&#xff0c;是这这个这…