# Interface **Repository Path**: make_a_summer/interface ## Basic Information - **Project Name**: Interface - **Description**: 接口自动化框架:Python+Pytest+Yaml-CSV+Allure+Log+Mysql+Mock - **Primary Language**: Python - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 47 - **Forks**: 5 - **Created**: 2024-12-15 - **Last Updated**: 2026-03-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: Python, pytest, Yaml ## README # Interface 沟通交流群聊: ![输入图片说明](README.assets/image1.png) ## 前言 该套框架在公司内部,已经写有500个接口,亲测可用。 本框架主要基于:Python+Pytest+Yaml+CSV+Redis+Allure+Logs+Mysql+Mock 实现接口自动化框架 Git地址:https://gitee.com/make_a_summer/interface.git 项目作者:唐松(挽一夏) 个人邮箱:tasng@foxmail.com (欢迎探讨学习进步) ## 实现功能 - 测试数据隔离,实现数据驱动。使用yaml文件,或者yaml+csv文件实现数据驱动; - 支持一键切换环境运行,只需修改config_manager.py文件即可,默认prod.yaml环境; - 接口关联,单个yaml文件支持内存缓存,多个yaml文件之间支持Redis跨文件传递,实现接口间数据依赖; - 接口数据提取,支持接口返回值的提取单个值,提取列表。支持自定义字段(请求参数)取值; - 接口动态断言,支持多种断言方式,支持多场景断言,可对提取值做动态断言; - 支持yaml用例执行前,提前执行SQL命令; - 支持用例熔断,创建草稿--提交订单--打印面单--订单发货。若提交订单失败,后续接口跳过; - 日志模块: 打印每个接口的日志信息; - 支持Mock接口; - 支持多线程运行,失败重试,达到最大失败次数熔断。可统计接口的运行时长; - 自定义拓展字段: 如用例中需要生成的随机数据,接口加密等,可直接调用; - 企业微信&邮件通知 ## 时序图 ```mermaid sequenceDiagram autonumber %% ================= 参与者全家福 (新增 RUN 和 NOTIFY) ================= participant CI as Jenkins/User participant RUN as run.py (启动入口) participant PT as Pytest (调度主进程) participant CONF as conftest.py (全局钩子) participant DATA as DataDriver (数据驱动) participant EXE as Executor (核心执行器) participant PSR as Parser (解析替换) participant MEM as 本地内存 (context_vars) participant REDIS as 全局缓存 (Redis) participant HTTP as HttpClient (网络层) participant MOCK as MockHook (挡板拦截) participant SVR as 目标服务器 (Target Server) participant EXT as Extractor (提取引擎) participant VLD as Validator (业务断言) participant ALLURE as Allure CLI (报告生成) participant NOTIFY as 企微/邮件 (消息通知) %% ================= 阶段一:run.py 启动与测试环境装配 ================= Note over CI, DATA: 🚀 阶段一:run.py 启动与数据驱动裂变 CI->>+RUN: 执行 `python run.py` (传入 AUTO_ENV 等环境变量) RUN->>RUN: 加载环境配置,清理本地旧的 allure-results 历史结果 RUN->>+PT: 调用 `pytest.main(['--alluredir=./allure-results'])` 移交控制权 PT->>CONF: 触发 pytest_sessionstart CONF->>REDIS: 建立全局连接池,清理历史脏数据 CONF->>CONF: 建立数据库连接池 PT->>DATA: 收集阶段 (pytest_generate_tests) alt 📄 纯 YAML 数据驱动 DATA->>DATA: 直接解析 YAML 内部 List 矩阵 else 📊 YAML 模板 + CSV 数据源 DATA->>DATA: 读取 CSV 外部数据注入 YAML 模板 end DATA-->>PT: 裂变生成 N 个完全独立的并发测试实例 (TestItem) PT->>CONF: 触发 pytest_runtest_setup (单用例前置) %% ================= 阶段二:单接口核心业务流水线 ================= Note over PT, VLD: 🚀 阶段二:单实例业务流水线 (核心执行层) PT->>+EXE: 下发单步测试数据 (Step Dict) Note over EXE, EXE: 🛡️ 启动 Deepcopy 深拷贝与 ExitStack 防 OOM 托管 opt 存在 setup_sql EXE->>PSR: 解析并执行前置 SQL 准备测试数据 end %% 1. 解析与替换 Note over EXE, REDIS: 1. 核心动态解析与替换 (Parser) EXE->>PSR: 移交原始载荷 (含 ${var}, {{redis_var}}) PSR->>MEM: 1. [优先] 读取 context_vars 局部变量 PSR->>REDIS: 2. [降级] 读取 Redis 全局跨链路变量 PSR->>PSR: 3. 反射执行内置函数 (如随机单号) PSR->>PSR: ⚡ 执行正则替换引擎,交付纯净物理载荷 PSR-->>EXE: 返回组装完成的 URL, Headers, Body %% 2. 网络发包与 Mock Note over EXE, SVR: 2. 网络请求分发与 Mock 拦截 EXE->>HTTP: 委派 HttpClient 发起调用 HTTP->>MOCK: 拦截点:检查是否命中 Mock 规则 alt 🛡️ 命中 Mock 规则 MOCK-->>HTTP: 阻断外发,移交伪造 FakeResponse else 🌐 未命中 Mock HTTP->>SVR: 携带真实载荷,发起底层 TCP/HTTP 请求 SVR-->>HTTP: 目标服务器返回真实 Response end HTTP-->>EXE: 统一移交携带底层物理信息的 Response 对象 EXE->>PT: 将 URL/Header/Body/耗时/原样返回 挂载到 Allure 内存快照 %% 3. 变量四大通道提取 (结合源码还原) Note over EXE, REDIS: 3. 全维变量提取引擎 (四大通道并行) par 通道 1 & 2: 单值提取 (extract & extract_local) EXE->>EXT: 执行单值正向/逆向提取 (阻断式搜索,取首个值) EXT-->>EXE: 写入 Redis全局 + MEM本地 / 仅写入 MEM本地 and 通道 3 & 4: 列表提取 (extract_list & extract_list_local) EXE->>EXT: 执行正则/JsonPath列表全量搜刮 EXT-->>EXE: 写入 Redis全局 + MEM本地 / 仅写入 MEM本地 end %% 4. 业务断言 Note over EXE, VLD: 4. 业务断言裁决 EXE->>PSR: ⚡ 翻译断言预期规则中的动态占位符 EXE->>VLD: 传入真实预期规则与实际 Response 进行比对 VLD-->>EXE: 裁决断言结果 (Pass / 抛出 AssertionError) %% 5. 后置清理 opt 存在 teardown_sql EXE->>EXE: 执行清理 SQL,还原环境数据 end Note over EXE, EXE: 🛡️ ExitStack 自动销毁触发,优雅释放 IO 资源 EXE-->>-PT: 结束本 Step 执行,上报测试状态 %% ================= 阶段三:报告收集与生成 ================= Note over PT, NOTIFY: 🚀 阶段三:测试收尾、生成报告与消息通知 PT->>CONF: 触发 pytest_runtest_teardown (用例级清理) PT->>CONF: 触发 pytest_runtest_makereport CONF->>CONF: 捕获异常,将崩溃日志/截图强行写入 allure-results PT->>CONF: 触发 pytest_sessionfinish (释放所有的 DB/Redis 连接池) PT-->>-RUN: pytest 执行完毕,进程退出,将状态码交还给 run.py RUN->>RUN: 检查 Pytest 退出状态码 RUN->>+ALLURE: 执行 `os.system('allure generate allure-results -o allure-report --clean')` ALLURE->>ALLURE: 渲染 HTML 静态网页数据 ALLURE-->>-RUN: 报告生成完毕 RUN->>NOTIFY: 调用 Webhook,推送测试通过率与 Allure 报告在线链接 RUN-->>-CI: run.py 完美结束,退出代码 0 或 1 ``` ## 运行接口框架前准备 ```tex 1. 本地电脑环境:安装Python3、JDK8、Allure包 2. 安装运行依赖库,pip install -r requirements.txt 3. 启动本地Redis、MySQL服务。并在prod.yaml文件中配置redis、mysql ``` Json/Yaml转换网站:[http://www.esjson.com/jsontoyaml.html](http://www.esjson.com/jsontoyaml.html) ## 目录架构 ```tex AutoFactor/ ├── business/ # 业务扩展层:存放与特定业务相关的自定义逻辑 │ └── debugtalk.py # 供 YAML 用例热调用的动态函数库 (如生成业务签名、时间戳等) ├── common/ # 底层基建层:跨项目通用的技术组件,无业务状态 │ ├── helpers/ # 辅助工具箱 │ │ └── id_generator.py # 用例序列号生成器 │ ├── notify/ # 消息通知中心 │ │ ├── email_sender.py # 邮件通知发送逻辑 │ │ ├── termianl_reporter.py # 控制台打印样式 │ │ └── wechat_sender.py # 企业微信机器人推送逻辑 │ ├── readers/ # 文件解析器 │ │ ├── csv_reader.py # 纯 CSV 数据加载工具 │ │ └── yaml_reader.py # 纯 YAML 文件加载工具 │ ├── exceptions.py # 全局自定义异常类 │ └── logger.py # 全局唯一日志对象,定义日志格式与输出 ├── configs/ # ⚙️ 全局配置层:环境隔离与配置访问中心 │ ├── envs/ # 环境数据文件夹 (存放 prod.yaml, alpha1.yaml 等) │ ├── config_manager.py # 唯一配置入口:提供对象化属性调用 (如 settings.mysql.host) │ └── env_reader.py # 底层工具:负责解析 YAML 配置文件 ├── data/ # 存放CSV格式文件 ├── files/ # 存放需要上传的文件 ├── logs/ # 📝 日志输出层:存放运行时的 .log 文件 ├── testcase/ # 🧪 用例数据层:存放具体的 YAML 用例与驱动脚本 ├── reports/ # 📊 报告输出层:存放生成的 Allure 原始数据与 HTML 报告 ├── util_tools/ # 核心执行引擎层:框架运行的灵魂 │ ├── core/ # 大脑:核心调度与解析逻辑 │ │ ├── cache_manager.py # 运行时变量池:管理多进程下的变量隔离与 Redis 存取 │ │ ├── data_driver.py # 数据驱动器:处理 CSV 参数化并裂变生成用例 │ │ ├── executor.py # 核心执行器:统筹生命周期、发送请求与结果流转 │ │ ├── extractor.py # 变量提取器:从响应中提取字段并存入变量池 │ │ ├── parser.py # 模板解析器:将 YAML 中的 ${var}、${get_extract_data(id)}替换为真实值 │ │ └── validator.py # 断言校验器:执行多规则的结果验证 │ ├── clients/ # 触手:负责所有对外通信的连接器 │ │ ├── http_client.py # HTTP 客户端:封装 Session 与请求重试 │ │ ├── mysql_client.py # 数据库客户端:连接池管理与 SQL 执行 │ │ └── redis_client.py # Redis 客户端:底层的缓存读写组件 │ ├── generators/ │ │ └── har_yaml_converter.py# 录制转换引擎:解析HAR抓包流量,智能降噪并生成标准 YAML 用例 │ └── hooks/ # 插件层:拦截并增强框架功能 │ ├── allure_hook.py # Allure 插件:动态生成测试步骤与附件 │ ├── mock_hook.py # Mock 插件:请求拦截与响应伪造 │ ├── sql_hook.py # SQL 插件:自动处理前后置数据库清理 │ └── stats_hook.py # 统计插件:收集运行数据并触发通知推送 ├── cli.py # 框架全局脚手架:命令行交互总入口 (提供 record 录制等指令分发) ├── pytest.ini # Pytest 核心配置文件 ├── requirements.txt # 项目 Python 依赖清单 └── run.py # ▶️ 框架启动总入口:集成环境初始化与测试启动 ``` ## 接口文档 > 微信公众号-标签管理: > > https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html > > ![img_1.png](README.assets/img_1.png) | **微信公众号-标签管理** | | ----------------------- | | 登录 | | 查询标签 | | 修改标签 | | 删除标签 | | 文件上传 | 偶然看见一位B站博主的视频,干货挺多的,推荐没有接触过Pytest框架的朋友先看一遍。本框架与该博主不一致,无参考效果。 > B站博主学习资料 > > [学习推荐](https://www.bilibili.com/video/BV1Um411y7mW/?share_source=copy_web&vd_source=548f70c25aefed2ddecfdc2796827683) ## 测试用例 ### 创建测试用例 在**testcase**目录下,分别创建两个文件,yaml文件存储测试数据,py文件运行。 比如==yaml_redis_业务流程==测试文件 - 创建YAML测试文件,编写接口数据 ```yaml - name: yaml_redis:创建标签 base_url: ${get_base_url(base_wx_url)} request: method: post path: /cgi-bin/tags/create?access_token=${get_extract_data(access_token)} json: tag: name: Tag${get_timestamp(100000, 999999)} extract: redis_tag_id: $.tag.id redis_tag_name: $.tag.name validation: - code: 200 - contain: ${get_extract_data(redis_tag_id)} ``` - 创建执行YAML的Py文件,进行读取YAML文件,发送接口请求 ```python @allure.feature(next(m_id) + "yaml+redis演示方法") class TestYamlRedis: @allure.story(next(c_id) + '微信标签创建业务') @allure.description('微信标签创建业务') @pytest.mark.parametrize('caseinfo', yaml_reader('testcase/yaml_redis_业务流程/yaml_reids.yaml')) def test_yaml_reids(self, caseinfo): allure.dynamic.title(caseinfo['name']) Executor().run_case(caseinfo) ``` ### YAML用例格式规范 YAML测试文件,编写接口数据,其中有严格的关键字层级,编写时需遵守 ```yaml - name: # 用例名称 base_url: # 用例基础URL request: headers: # 请求头 (可选) cookies: # cookies (可选) method: # 请求方式 path: # 接口请求路径 data/json/params: # 请求参数 (可选) extract: # 提取参数到redis (可选) # extract_list: # 提取参数列表到redis # extract_local: # 提取参数到缓存 # extract_list_local: # 提取参数列表到缓存 validation: # 断言 ``` **必填项:** ​ 4个一级关键字:name、base_url、request、validation ​ 2个二级关键字:method、path ### YAML用例数据编写 一个YAML文件管理,能管理一组或者多组数据。YAML文件中有几组数据,就发送几次请求。 ​ yaml文件中有5组测试数据,所以能执行5次请求 ~~~YAML - name: yaml_redis:创建标签 base_url: ${get_base_url(base_wx_url)} request: method: post path: /cgi-bin/tags/create?access_token=${get_extract_data(access_token)} json: tag: name: Tag${get_timestamp(100000, 999999)} extract: redis_tag_id: $.tag.id redis_tag_name: $.tag.name validation: - code: 200 - contain: ${get_extract_data(redis_tag_id)} - name: yaml_redis:查询标签 base_url: ${get_base_url(base_wx_url)} request: method: post path: /cgi-bin/tags/get?access_token params: access_token: ${get_extract_data(access_token)} extract: # 单步重试配置 retry: times: 5 # 最多重试 5 次 interval: 3 # 每次间隔 3 秒 validation: - code: 200 - contain: name - name: yaml_redis:编辑标签 base_url: ${get_base_url(base_wx_url)} request: method: post path: /cgi-bin/tags/update?access_token= ${get_extract_data(access_token)} json: tag: id: ${get_extract_data(redis_tag_id)} name: test${get_timestamp(100000, 999999)} extract: validation: - code: 200 - contain: ok - name: yaml_redis:删除标签 base_url: ${get_base_url(base_wx_url)} request: method: post path: /cgi-bin/tags/delete?access_token=${get_extract_data(access_token)} json: tag: id: ${get_extract_data(redis_tag_id)} extract: validation: - code: 200 - contain: ok - name: yaml_redis:文件上传 base_url: ${get_base_url(base_wx_url)} request: method: post path: /cgi-bin/media/uploadimg?access_token=${get_extract_data(access_token)} files: files: 'files/mag.jpg' extract: validation: - code: 200 - contain: url ~~~ ![image-20260308234315676](./README.assets/image-20260308234315676.png) ### YAML+CSV管理用例 看上方发现,在YAML文件中管理多组测试数据,一组两组还好,多了之后YAML中数据就过多,不好查看与管理。 所有就引入了CSV管理,对可变的参数进行处理 - 1、CSV文件存储在data目录下 - 2、使用CSV方式做参数化,在YAML文件中,要做对应修改,新增关键字`parameters`,与对应的读取数据格式$csv{XXX} 下面使用同一个接口,展示CSV管理用例 1、data目录下,新建CSV文件,有四个字段需要被替换使用 ~~~CSV name,method,validation 正常创建_创建标签1,post,${get_extract_data(003_tag_id)} 正常创建_创建标签2,post,${get_extract_data(003_tag_name)} 创建异常_请求方式错误,get,44002 ~~~ 2、YAML测试文件,加入 **parameters** 字段,并将csv文件路径传入。被替换的字段用$csv{XXX}格式 ```yaml - name: $csv{name} base_url: ${get_base_url(base_wx_url)} # 使用csv格式,就要加入'parameters'字段。需要替换的数据使用$csv{}进行替换。 # name-method-validation 要一一对应csv文件的表头 parameters: name-method-validation: data/wx_tag_create.csv request: method: $csv{method} path: /cgi-bin/tags/create?access_token=${get_extract_data(access_token)} json: tag: name: Tag${get_timestamp(100000, 999999)} extract: 003_tag_id: $.tag.id 003_tag_name: $.tag.name validation: - contain: $csv{validation} ``` ![image-20260308234738356](./README.assets/image-20260308234738356.png) ### 使用Har文件生成Yaml用例 解决的痛点:业务流程比较长,多个接口需要每次都要抓取接口参数,从JSON格式转换Yaml格式的请求参数。下面是具体的演示 该接口未开放。请各位根据自己的项目实际替换。 第一步,在浏览器页面操作,并筛选接口后,下载har文件,可放置data目录下 ![image-20260312002132980](./README.assets/image-20260312002132980.png) ![image-20260312002327995](./README.assets/image-20260312002327995.png) 第二步:在cli.py文件中修改Yaml文件生成路径后。直接在控制台用命令生成Yaml用例 python cli.py record data/流量回放.har ~~~Python record_parser.add_argument( "-o", "--output", type=str, default="testcase/auto_gen", # --->Yaml文件生成路径 help="生成的 YAML 用例存放目录 (默认: testcase/auto_gen)" ) ~~~ 或者在控制台中,使用命令控制 ~~~shell python cli.py record data/流量回放.har # 就是默认生成Yaml文件至testcase/auto_gen python cli.py record data/流量回放.har -o testcase/har_case # 生成Yaml文件到指定目录testcase/har_case ~~~ ![image-20260312003019654](./README.assets/image-20260312003019654.png) 第三步:改改Yaml文件。把数据参数动态替换。然后复制生成一个py文件,正常走测试流程。 ![image-20260312004627887](./README.assets/image-20260312004627887.png) ## 用例中提取参数 #### 提取方式介绍 根据接口实际结果使用。均支持使用**jsonpath**,**正则表达式** ##### 提取参数至Redis中 ​ extract :提取单个数据 ​ extract_list : 提取的数据是列表 ##### 提取参数至内存缓存中: ​ extract_local: 提取单个数据 ​ extract_list_local: 提取的数据是列表 ##### 内存缓存与Redis适用场景 内存缓存适用场景: ​ 一个yaml文件中,写了完整的接口业务流程,那么使用内存缓存是最合适的,接口提取参数,只在yaml文件内接口中使用,用完即销毁。 Redis缓存适用场景: ​ 一个yaml文件中写一个接口,py文件中存在不同yaml接口用例,接口直接关联参数,需要跨文件传参,那么使用redis来存储是最合理的。 ### 提取单个数据-Redis #### 提取接口返回值,指定字段。 ##### 使用jsonpath提取 extract字段中,根据接口返回值,写入jsonpath表达式 ~~~yaml - name: yaml_redis:创建标签 base_url: ${get_base_url(base_wx_url)} request: method: post path: /cgi-bin/tags/create?access_token=${get_extract_data(access_token)} json: tag: name: Tag${get_timestamp(100000, 999999)} extract: redis_tag_id: $.tag.id redis_tag_name: $.tag.name validation: - code: 200 - contain: ${get_extract_data(redis_tag_id)} ~~~ 接口返回值: ~~~json {"tag":{"id":466,"name":"Tag820515"}} ~~~ 接口运行成功后,提取存入至Redis中 ![image-20260309000959035](./README.assets/image-20260309000959035.png) ##### 使用正则表达式提取 extract字段中,根据接口返回值,写入正则表达式 ~~~yaml - name: yaml_redis:创建标签 base_url: ${get_base_url(base_wx_url)} request: method: post path: /cgi-bin/tags/create?access_token=${get_extract_data(access_token)} json: tag: name: Tag${get_timestamp(100000, 999999)} extract: # 正则提取数字 id redis_tag_id: '"id":\s*(\d+)' # 正则提取字符串 name redis_tag_name: '"name":\s*"(.*?)"' validation: - code: 200 - contain: ${get_extract_data(redis_tag_id)} ~~~ 运行成功后,成功写入Redis中 ##### 提取指定参数 当接口返回多组数据,只要其中符合条件的字段 yaml文件中,创建标签接口---查询接口。创建标签后,根据标签名称提取标签的id ~~~yaml - name: yaml_redis:创建标签 base_url: ${get_base_url(base_wx_url)} request: method: post path: /cgi-bin/tags/create?access_token=${get_extract_data(access_token)} json: tag: name: Tag${get_timestamp(100000, 999999)} extract: redis_tag_id: $.tag.id redis_tag_name: $.tag.name validation: - code: 200 - contain: ${get_extract_data(redis_tag_id)} - name: yaml_redis:查询标签 base_url: ${get_base_url(base_wx_url)} request: method: post path: /cgi-bin/tags/get?access_token params: access_token: ${get_extract_data(access_token)} extract: caseid: "$.tags[?(@.name=='${get_extract_data(redis_tag_name)}')].id" validation: - code: 200 - contain: name ~~~ ~~~json {"tags":[{"id":2,"name":"星标组","count":0},{"id":411,"name":"Tag128","count":0},{"id":412,"name":"Tag726399","count":0},{"id":413,"name":"Tag997593","count":0},{"id":416,"name":"Tag187","count":0},{"id":417,"name":"Tag423","count":0},{"id":418,"name":"Tag931269","count":0},{"id":419,"name":"Tag978659","count":0},{"id":421,"name":"Tag672160","count":0},{"id":422,"name":"Tag397593","count":0},{"id":424,"name":"Tag425","count":0},{"id":429,"name":"Tag163733","count":0},{"id":430,"name":"Tag789642","count":0},{"id":431,"name":"test769","count":0},{"id":432,"name":"test857819","count":0},{"id":433,"name":"Tag161445","count":0},{"id":434,"name":"Tag934713","count":0},{"id":435,"name":"Tag320499","count":0},{"id":436,"name":"Tag702782","count":0},{"id":437,"name":"Tag359850","count":0},{"id":438,"name":"Tag496765","count":0},{"id":439,"name":"test310135","count":0},{"id":440,"name":"test811199","count":0},{"id":441,"name":"Tag480","count":0},{"id":442,"name":"test648","count":0},{"id":443,"name":"Tag576","count":0},{"id":444,"name":"Tag462827","count":0},{"id":445,"name":"Tag575675","count":0},{"id":446,"name":"test745221","count":0},{"id":447,"name":"test750","count":0},{"id":448,"name":"Tag626","count":0},{"id":449,"name":"Tag465","count":0},{"id":450,"name":"Tag387561","count":0},{"id":451,"name":"Tag801753","count":0},{"id":452,"name":"test968295","count":0},{"id":453,"name":"test414142","count":0},{"id":454,"name":"test359","count":0},{"id":455,"name":"test229789","count":0},{"id":456,"name":"Tag587032","count":0},{"id":457,"name":"Tag599066","count":0},{"id":458,"name":"test570217","count":0},{"id":459,"name":"Tag498482","count":0},{"id":460,"name":"Tag221366","count":0},{"id":461,"name":"Tag771105","count":0},{"id":462,"name":"Tag241545","count":0},{"id":463,"name":"Tag434436","count":0},{"id":464,"name":"Tag792478","count":0},{"id":465,"name":"Tag359792","count":0},{"id":466,"name":"Tag820515","count":0},{"id":467,"name":"Tag565251","count":0},{"id":468,"name":"Tag704110","count":0}]} ~~~ 指定提取name = Tag704110下的id,存入Redis中 ![image-20260309001927955](./README.assets/image-20260309001927955.png) #### 提取单个数据-内存缓存 一个yaml文件中编写用例,使用内存缓存存储接口关联数据 ~~~yaml - name: yaml_内存关联:创建微信标签 base_url: ${get_base_url(base_wx_url)} # 定义在该流程内通用的初始变量,供下方所有步骤调用 variables: {} # 业务步骤列表 (steps),框架会按照列表顺序,从上到下逐一执行 steps: - name: yaml_cache:创建标签 request: method: post path: /cgi-bin/tags/create?access_token=${get_extract_data(access_token)} json: tag: name: Tag${get_timestamp(100000, 999999)} # 提取单个值 extract_local: csv_tag_id: $.tag.id csv_tag_name: $.tag.name validation: - code: 200 - contain: ${csv_tag_id} - name: yaml_cache:查询标签 request: method: post path: /cgi-bin/tags/get?access_token params: access_token: ${get_extract_data(access_token)} # 提取列表 extract_list_local: all_ids: "$.tags[*].id" validation: - code: 200 - contain: name ~~~ ![image-20260309002806829](./README.assets/image-20260309002806829.png) ### 提取列表-Redis 查询标签接口,返回值中所有的**id**。分别使用正则与jsonpath提取,使用**extract_list**关键字 接口返回 ```json { "tags": [ { "id": 2, "name": "星标组", "count": 0 }, { "id": 100, "name": "22214767", "count": 0 }, { "id": 129, "name": "case", "count": 0 }, { "id": 130, "name": "case60", "count": 0 }, { "id": 131, "name": "case86", "count": 0 }, { "id": 153, "name": "哈哈哈63", "count": 0 }, { "id": 154, "name": "哈哈哈45", "count": 0 }, { "id": 155, "name": "哈哈哈54", "count": 0 } ] } ``` 正则提取,提取列表使用extract_list关键字,提取所有id值 ```yaml - name: wx-查询标签 base_url: ${get_base_url(base_wx_url)} request: method: post path: /cgi-bin/tags/get?access_token params: access_token: ${get_extract_data(access_token)} extract_list: all_ids: '"id":\s*(\d+)' validation: - code: 200 - contain: name ``` jsonpath提取,提取列表使用extract_list关键字,提取所有goodsId值 ```yaml - name: wx-查询标签 base_url: ${get_base_url(base_wx_url)} request: method: post path: /cgi-bin/tags/get?access_token params: access_token: ${get_extract_data(access_token)} extract_list: all_ids: "$.tags[*].id" validation: - code: 200 - contain: name ``` 运行成功,存入Redis中 ![img_4.png](README.assets/img_4.png) ### 接口参数使用 #### 单个参数使用 这一步就是接口关联的实际用法。上一个接口返回值中提取后存入,下一个接口中读取使用 固定格式写法:${get_extract_data(xxx)} 比如查询标签---编辑标签接口 查询标签,提取name = case86的id,编辑标签中使用该id ~~~yaml - name: wx-编辑标签 base_url: ${get_base_url(base_wx_url)} request: method: post path: /cgi-bin/tags/update?access_token= ${get_extract_data(access_token)} json: tag: id: ${get_extract_data(caseid)} name: test${get_random_number(100000, 999999)} extract: validation: - code: 200 - contain: ok ~~~ #### 多个参数使用 上面提取了列表,并存储,那么就到了使用的时候 Redis中,存入多个值,yaml文件中引用。 id: ${get_extract_data(all_ids,1)},根据索引取第一个值。0随机取值,-1取全部值。方法在debugtalk.py文件中,可以自行扩展。 ```yaml - name: wx-编辑标签 base_url: ${get_base_url(base_wx_url)} request: method: post path: /cgi-bin/tags/update?access_token= ${get_extract_data(access_token)} json: tag: id: ${get_extract_data(all_ids,1)} name: test${get_random_number(100000, 999999)} extract: validation: - code: 200 - contain: ok ``` ## 用例中接口传参如何实现 1、通过上个接口发送请求,得到token请求值,存储到redis中 2、YAML文件中,通过debugtalk.py文件中,使用read_extract_redis方法,获取redis中数据 读取关联值,因为提取的有单个数据、列表数据,所以提取的方式略有不同 单个参数,如上述`token`值,在YAML文件中,使用${get_extract_data(token)} 列表参数,如上述`goodIds`第1个值,在YAML文件中,使用${get_extract_data(goodIds,0)} redis中,已经存储数据 编写方式,需要替换取extract.yaml文件值的地方,使用$ {get_extract_data(xxx)},列表使用${get_extract_data(xxx,位数)} ```yaml # 微信删除标签接口,如需传入token params: token: ${get_extract_data(token)} # 编辑标签中,如想编辑第1个标签,就取第一个id值:2 json: id: ${get_extract_data(all_ids,1)} ``` ## 单用例重试 解决问题:因为网络抖动导致的其中一个接口运行失败。或者当前接口因为需要获取某些数据获取到,才能进行下一步。 比如打印面单,得到物流商返回的运单号后,才能打印面单,那么在yaml中增加关键字**retry** ~~~yaml - name: yaml_redis:查询标签 base_url: ${get_base_url(base_wx_url)} request: method: post path: /cgi-bin/tags/get?access_token params: access_token: ${get_extract_data(access_token)} extract: caseid: "$.tags[?(@.name=='${get_extract_data(redis_tag_name)}')].id" # 新增单步重试配置 retry: times: 5 # 最多重试 5 次 interval: 3 # 每次间隔 3 秒 validation: - code: 200 - contain: name ~~~ ## 接口执行前执行SQL 在接口执行之前,需要造数据、清理数据 ~~~yaml - name: yaml_redis:查询标签 base_url: ${get_base_url(base_wx_url)} # 接口运行之前,执行SQL setup_sql: - "UPDATE t_sku SET `name` = 'test022520' WHERE id = 2101100417" request: method: post path: /cgi-bin/tags/get?access_token params: access_token: ${get_extract_data(access_token)} extract: caseid: "$.tags[?(@.name=='${get_extract_data(redis_tag_name)}')].id" # 新增单步重试配置 retry: times: 5 # 最多重试 5 次 interval: 3 # 每次间隔 3 秒 validation: - code: 200 - contain: name # 接口运行之后,执行SQL teardown_sql: - "DELETE FROM t_temu_y2_order_17 WHERE erp_no = 'ZY4H20250731001'" ~~~ ## 用例中函数扩展使用 在使用的过程中,发现需要扩展一些方法函数,可以在debugtalk.py文中编写实现。比如看例子时候发现,URL、headers、cookies都是${xx()} 格式。这就是在debugtalk.py文件中定义了写函数,并进行替换使用。如 `${get_base_url()}、${get_headers()}、 ${get_extract_data(Cookie)}` 这些方法均在debugtalk.py文件中编写,yaml用例中使用。 ```python class DebugTalk: # 基础url @classmethod def get_base_url(cls, node_name): """ :param node_name: base_url :return: """ return read_config_file('base', node_name) # mysql数据库 @classmethod def get_mysql_config(cls, node_name): """ :param node_name: mysql :param node_name: :return: """ return read_config_file('mysql', node_name) # headers请求头 @classmethod def get_headers(cls, params_type): headers_mapping = { 'data': {'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'}, 'json': {'Content-Type': 'application/json;charset=UTF-8 '} } header = headers_mapping.get(params_type) if header is None: raise ValueError('不支持其他类型的请求头设置') return header # 随机数 @classmethod def get_random_number(cls, min, max): return random.randint(int(min), int(max)) ``` 微信创建标签接口时,就可以使用随机数函数 ```yaml - name: wx-创建标签 base_url: ${get_base_url(base_wx_url)} request: method: post path: /cgi-bin/tags/create?access_token=${get_extract_data(access_token)} json: tag: name: test${get_random_number(10000, 99999)} extract: validation: - code: 200 ``` ## 断言类型 本框架只实现了五种类型断言 `状态码断言、包含模式断言、相等断言、不相等断言、数据库断言` ```yaml validation: - code: 200 # 接口的响应状态码断言 - contain: access_token # 包含模式 - eq: { 'expires_in': 7200 } # 相等断言 - ne: { 'expires_in': 72001111 } # 不相等断言 - db: # 数据库断言 sql: "SELECT erp_no FROM t_b2b_outbound WHERE puid=145 AND outbound_no='${get_extract_data(b2b_outboundNo)}'" # SQL语句 eq: {erp_no: '${get_extract_data(b2b_outbound_erpNo)}'} # 相等断言,期望值=SQL语句 - db: sql: "SELECT erp_no,outbound_no FROM t_b2b_outbound WHERE puid=145 AND outbound_no='${get_extract_data(b2b_outboundNo)}'" # SQL语句 contain: { outbound_no: '${get_extract_data(b2b_outboundNo)}' } # 包含断言,期望值在查询的SQL语句内 ``` ## 熔断机制 5个接口,其中第3个接口运行失败,后续接口会直接跳过 ![image-20260309003621161](./README.assets/image-20260309003621161.png) ## allure报告 执行run.py文件,在reports文件allures文件下,打开index.html文件,即可查看 收到邮箱通知 ![img.png](README.assets/img2.png)