# 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**: 20 - **Forks**: 1 - **Created**: 2024-12-15 - **Last Updated**: 2025-09-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: Python, pytest, Yaml ## README # Interface ## 前言 简单讲述框架的使用 本框架主要基于:Python+Pytest+Yaml(Yaml+CSV)+Allure+Log+Mysql,实现接口自动化框架 Git地址:https://gitee.com/make_a_summer/interface.git 项目作者:唐松(挽一夏) 个人邮箱:tasng@foxmail.com (欢迎探讨学习进步) ## 实现功能 - 测试数据隔离,实现数据驱动。使用yaml文件,或者yaml-csv文件实现单接口多组数据 - 支持多接口数据依赖:如A接口需要同时依赖B、C接口的响应数据作为参数。提取接口单个值,提取接口返回列表 - 数据库断言:直接在测试用例中写入查询的sql即可断言,无需编写代码 - 动态多断言: 如接口需要同时校验响应数据和sql校验,支持多场景断言 - 日志模块: 打印每个接口的日志信息 - 简单mock接口 - 统计接口的运行时长 - 自定义拓展字段: 如用例中需要生成的随机数据,可直接调用 ## TODO - [x] 测试数据管理优化。全框架只使用一个extract.yaml进行接口数据关联存储。全局共享,生命周期不可控,导致数据污染或无法跨用例共享。 - [x] 用例跳过、重跑机制 - [x] 代码耦合度高,进行抽象 - [x] 用例执行优先级,并发执行 ## 目录架构 ``` |-common // 辅助类,用于整个测试框架 | |-debugtalk.py // 自定义函数供测试脚本调用。生成随机数、日期时间戳等常用的功能 |-config // 配置 | |-config.py // 读取项目配置文件-config.yaml | |-config.yaml // 项目配置文件 | |-extract.yaml // 存储接口数据关联 | |-setting.py // 项目基础路径 |-data // CSV测试文件 |-files // 上传文件 |-logs // 日志 |-reports // 测试报告 |-testcase // 测试用例 |-util_tools // 工具库文件 | └──db_connector // 数据库模块 | └──connectMysql.py // MySQL工具类 | └── headle_data // 测试文件操作模块 | └── csv_anlaysis_params.py // Yaml文件使用CSV格式,进行数据处理 | └── csv_handler.py // 读取CSV文件 | └── yaml_handler.py // 读取Yaml文件 | └── other_util // 其他工具类 | └── allure_type.py // Allure报告步骤中,数据格式处理 | └── genrate_id.py // 测试用例ID | └── yaml_process // Yaml文件处理 | └── yaml_analysis.py // Yaml数据规范、替换、发送请求、数据提取、执行断言 | └── yaml_extract.py // 处理数据提取 | └── yaml_replace.py // 处理数据替换 | └── assertion_util.py // 封装断言 | └── mock_util.py // 封装mock | └── logger_util.py // 封装日志 | └── requests_util.py // 封装发送请求 |-conftest.py // 运行前之前操作,清空extract.yaml文件 |-pytest.ini |-environment.xml // allure报告中环境配置 |-run.py // 运行入口 ``` ## 接口文档 > 微信公众号-标签管理: > https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html 1. 登录 2. 查询标签 3. 创建标签 4. 修改标签 5. 删除标签 6. 文件上传 > B站博主学习资料,且配套测试接口(强烈推荐)。 > > [https://b23.tv/0Qlxedq](https://gitee.com/link?target=https%3A%2F%2Fb23.tv%2F0Qlxedq) > > 博主第一期视频评论区,有练习接口 ## 测试用例 ### 创建测试用例 在testcase目录下分别创建**两个**文件,yaml文件存储测试数据,py文件运行。 ​ 比如在testcase目录userManager文件下新建==用户查询==接口测试 - 创建YAML测试文件,编写接口数据 ```yaml - name: 用户查询 base_url: ${get_base_url(base_url)} request: headers: ${get_headers(data)} method: post path: /dar/user/queryUser data: user_id: 123839387391912 extract: validation: - code: 200 - contain: { "msg": "查询成功!" } ``` - 创建执行YAML的Py文件,进行读取YAML文件,发送接口请求 ```python @allure.feature(next(m_id) + '用户管理模块') class TestUserManager: @allure.story(next(c_id) + '用户查询') @allure.description('用户查询接口') @pytest.mark.parametrize('caseinfo', read_testcase_file('testcase/userManager/queryUser.yaml')) def test_user_query(self, caseinfo): allure.dynamic.title(caseinfo['name']) YamlAnalysis().yaml_analysis_execution(caseinfo) ``` ### YAML用例格式规范 YAML测试文件,编写接口数据,其中有严格的==关键字层级==,编写时需遵守 ```yaml - name: # 用例名称 base_url: # 用例基础URL request: headers: # 请求头 (可选) cookies: # cookies (可选) method: # 请求方式 path: # 接口请求路径 data/json/params: # 请求参数 (可选) extract: # 提取参数 (可选) # extract_list: # 提取参数列表 validation: # 断言 ``` **必填项:** ​ 4个一级关键字:name、base_url、request、validation ​ 2个二级关键字:method、path 选填项: ​ extract:提取接口返回值中,单个key的值 ​ extract_list:提取接口返回值中,指定key的所有值列表 ​ ### YAML用例数据编写 一个YAML文件管理,能管理一组或者多组数据。YAML文件中有几组数据,就发送几次请求。 ​ yaml文件中有两组测试数据,所以能执行两次请求 ~~~YAML - name: wx_获取token成功 base_url: ${get_base_url(base_wx_url)} request: method: post path: /cgi-bin/token data: grant_type: client_credential appid: wx2a3d01a57cd117f4 secret: 938fa8f53264eecf325dcd67546c747e extract: access_token: $.access_token validation: - contain: expires_in - name: wx_获取token失败 base_url: ${get_base_url(base_wx_url)} request: method: post path: /cgi-bin/token data: grant_type: appid: wx2a3d01a57cd117f4 secret: 938fa8f53264eecf325dcd67546c747e extract: validation: - contain: errcode ~~~ ![image-20250801182208054](C:\Users\Administrator\Desktop\interface1\README.assets\image-20250801182208054.png) ### YAML+CSV管理用例 看上方发现,在YAML文件中管理多组测试数据,一组两组还好,多了之后YAML中数据就过多,不好查看与管理。 所有就引入了CSV管理,对可变的参数进行处理 - 1、CSV文件存储在data目录下 - 2、使用CSV方式做参数化,在YAML文件中,要做对应修改,新增关键字`parameters`,与对应的读取数据格式$csv{XXX} 下面使用同一个接口,展示CSV管理用例 1、data目录下,新建CSV文件,有四个字段需要被替换使用 ~~~CSV name,appid,secret,grant_type,validation csv-获取token,wx2a3d01a57cd117f4,938fa8f53264eecf325dcd67546c747e,client_credential,access_token csv-appid必须检查,"",938fa8f53264eecf325dcd67546c747e,client_credential,errcode csv-appid错误检查,111111111111111,938fa8f53264eecf325dcd67546c747e,client_credential,errcode csv-secret缺失,wx2a3d01a57cd117f4,"",client_credential,errcode csv-secret错误检查,wx2a3d01a57cd117f4,22222222222222,client_credential,errcode csv-grant_type缺失,wx2a3d01a57cd117f4,938fa8f53264eecf325dcd67546c747e,"",errcode csv-grant_type错误检查,wx2a3d01a57cd117f4,938fa8f53264eecf325dcd67546c747e,1111111111111,errcode ~~~ 2、YAML测试文件,加入==parameters==字段,并将csv文件路径传入。被替换的字段用$csv{XXX}格式 ```yaml - name: $csv{name} base_url: ${get_base_url(base_wx_url)} # 使用csv格式,就要加入'parameters'字段。需要替换的数据使用$csv{}进行替换 parameters: name-appid-secret-grant_type-validation: data/wx_tag_login.csv request: method: post path: /cgi-bin/token data: appid: $csv{appid} secret: $csv{secret} grant_type: $csv{grant_type} extract: access_token: $.access_token validation: - contain: $csv{validation} ``` ![image-20250801182331132](C:\Users\Administrator\Desktop\interface1\README.assets\image-20250801182331132.png) ## 用例中提取参数 提取参数有两个方式,根据接口实际结果使用。支持使用**jsonpath**,**正则表达式**。提取的接口关联参数存储到:==extract.yaml==文件中 extract :提取单个数据 extract_list : 提取的数据是列表 ### 提取单个数据 - 使用jsonpath提取,上面例子中有写到 - 使用正则提取,根据接口返回值写正则表达式提取 ### 提取列表 获取商品列表接口,返回值中提取**goodsId**,分别使用正则与jsonpath提取,使用==extract_list==关键字 - 使用jsonpath提取 - 使用正则提前 ```json { "api_info": "today:21 max:10000 all[90=21+33+36];expires:2030-12-31", "cache": 0, "error_code": "0000", "goodsList": [{ "goodsId": "18382788819", "goods_count": "233", "goods_image": "https://omsproductionimg.yangkeduo.com/images/2017-12-12/bcf848aa71c6389607ae7a84b70f1543.jpeg", "goods_name": "【2件套】套装秋冬新款仿獭兔毛钉珠皮草毛毛短外套加厚大衣女装", "original_price": "", "unit_price": "¥99.00" }, { "goodsId": "33809635011", "goods_count": "521", "goods_image": "https://omsproductionimg.yangkeduo.com/images/2017-12-12/176019babfdecffa1d9f98f40b7e99b4.jpeg", "goods_name": "好奇小森林心钻装纸尿裤M22拉拉裤L18/XL14超薄透气裤型尿不湿 1件装", "original_price": "", "unit_price": "¥108.00" }, { "goodsId": "56996760797", "goods_count": "1181", "goods_image": "https://omsproductionimg.yangkeduo.com/images/2017-12-12/efb5db42397550bffd3211ca6f197498.jpeg", "goods_name": "冻干鸡小胸整块增肥营养发腮狗狗零食新手养猫零食幼猫零食100g", "original_price": "", "unit_price": "¥17.80" }, { "goodsId": "82193785267", "goods_count": "3000+", "goods_image": "https://omsproductionimg.yangkeduo.com/images/2017-12-12/efb5db42397550bffd3211ca6f197498.jpeg", "goods_name": "【自营】ISB伊珊娜意大利水果系列宠物犬猫沐浴露除臭香波护毛素", "original_price": "", "unit_price": "¥650.00" }, { "goodsId": "74190550836", "goods_count": "1000+", "goods_image": "https://omsproductionimg.yangkeduo.com/images/2017-12-12/efb5db42397550bffd3211ca6f197498.jpeg", "goods_name": "【新品零0CM嵌入式】海尔电冰箱410L家用法式四门多门官方正品", "original_price": "", "unit_price": "¥5746.00" }], "reason": "", "request_id": "request_id", "secache": "c98b29872e8a4b28859db207944ba817", "secache_date": "2025-08-03 19:25:45", "secache_time": 1754220345000, "translate_language": "zh-CN" } ``` 正则提取,提取列表使用==extract_list==关键字 ```yaml - name: 获取商品列表-正常获取 base_url: ${get_base_url(base_url)} request: headers: ${get_headers(data)} method: get path: /coupApply/cms/goodsList params: msgType: getHandsetListOfCust page: 1 size: 20 extract_list: goodIds: '"goodsId":\s*"(\d+)"' validation: - code: 200 ``` jsonpath提取,提取列表使用==extract_list==关键字 ```yaml - name: 获取商品列表-正常获取 base_url: ${get_base_url(base_url)} request: headers: ${get_headers(data)} method: get path: /coupApply/cms/goodsList params: msgType: getHandsetListOfCust page: 1 size: 20 extract_list: good_ids: $.goodsList[*].goodsId validation: - code: 200 - contain: 'api_info' ``` 运行成功,存入extract.yaml文件中 ```yaml good_Ids: - '18382788819' - '33809635011' - '56996760797' - '82193785267' - '74190550836' ``` ## 用例中接口传参如何实现 1、通过上个接口发送请求,得到token请求值,存储到extract.yaml文件中 2、YAML文件中,通过debugtalk.py文件中,使用get_extract_data方法,获取extract.yaml文件数据 读取extract.yaml值,因为提取的有单个数据、列表数据,所以提取的方式略有不同 单个参数,如上述`token`值,在YAML文件中,使用${get_extract_data(token)} 列表参数,如上述`goodIds`第1个值,在YAML文件中,使用${get_extract_data(goodIds,0)} extract.yaml文件中,已经存储数据 ```yaml good_ids: - '18382788819' - '33809635011' - '56996760797' - '82193785267' - '74190550836' access_token: 94_ETMcClvYuBMDFr32wATsmLVJ_rigjW2MLH8NdrYhIBa8UaeM43bfVUiwz8oS1OsQ5buedR3uoqSx0q5bywACwDJqPSkt9UYNSBS6AAGnjs2dnXJ59SCLwVdZunYURIgAHAOLC ``` 编写方式,需要替换取extract.yaml文件值的地方,使用${get_extract_data(xxx)},列表使用${get_extract_data(xxx,位数)} ```yaml # 微信删除标签接口,如需传入token params: token: ${get_extract_data(token)} # 商品详情中,如想看第1个商品,就取第一个good_ids值:18382788819 json: pro_id: ${get_extract_data(goodIds,0)} ``` ## 用例中需要cookie 有的接口发送时需要携带cookie信息。 若cookie存储到extract.yaml文件中,在使用${get_extract_data(xxx)}进行数据获取 ~~~yaml - name: 获取物料信息 base_url: ${get_base_url(base_url)} request: headers: ${get_headers(data)} cookies: ${get_extract_data(Cookie)} method: get path: /api/order/customer/orderPlan/getMaterial validation: - eq: { 'message': '操作成功' } - code: 200 extract_list: materIds: $.material[*] ~~~ ## 用例中函数扩展使用 在使用的过程中,发现需要扩展一些方法函数,可以在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: - contain: {'msg': '登录成功'} # 包含模式 - code: 200 # 接口的响应状态码断言 - eq: {'msg': '登录成功'} # 相等断言 - ne: {'msg': '登录失败'} # 不相等断言 - db: SELECT name, fee FROM testcase.`user` WHERE id=4; # 数据库断言 ``` ## allure报告 执行run.py文件,在reports文件allures文件下,打开index.html文件,即可查看 ## 运行流程详解 ### ✅ **1. 启动入口:`run.py`** ```python if __name__ == '__main__': pytest.main() # 运行 pytest 收集并执行测试用例 shutil.copy('environment.xml', './reports/temps') # 复制环境信息 os.system("allure generate reports/temps -o reports/allures --clean") # 生成 Allure 报告 ``` ------ ### ✅ **2. pytest 收集用例(根据 `pytest.ini` 配置)** - **测试路径**:`testcase/` - **文件命名**:`test_*.py` - **类命名**:`Test*` - **函数命名**:`test_*` - **并发执行**:`-n auto`(pytest-xdist) - **报告缓存**:`--alluredir=reports/temps` ------ ### ✅ **3. 测试用例结构(YAML 驱动)** 每个测试用例是一个 YAML 文件,结构如下: ```yaml name: 创建订单接口 base_url: ${get_base_url(base_oms_url)} request: method: POST path: /oms/order/create headers: ${get_headers(json)} json: skuId: ${get_extract_data(create_sku_id)} validation: - code: 200 - eq: success: true extract: orderId: $.data.orderId mock: status_code: 200 json: success: true ``` ------ ### ✅ **4. 用例解析与执行流程:`YamlAnalysis.yaml_analysis_execution()`** | 步骤 | 说明 | | :------------------- | :----------------------------------------------------------- | | **① 读取 YAML 文件** | `yaml_handler.py` → `read_testcase_file()` | | **② 参数化替换** | `csv_analysis_params.py` → CSV 数据替换 `$csv{key}` | | **③ 变量替换** | `yaml_replace.py` → 替换 `${get_extract_data(key)}` 等 | | **④ Mock 判断** | `mock_util.py` → 如果存在 `mock:` 字段,则替换真实请求 | | **⑤ 发送请求** | `requests_util.py` → `execute_request()` | | **⑥ 提取响应值** | `yaml_extract.py` → `extract_data()` / `extract_data_list()` | | **⑦ 断言验证** | `assertion_util.py` → `assert_result()` | | **⑧ Allure 报告** | `allure.attach()` 记录请求、响应、断言、提取数据 | ------ ### ✅ **5. 断言支持类型** | 断言类型 | 示例 | | :--------- | :-------------------------------------------- | | 状态码断言 | `code: 200` | | 包含断言 | `contain: success` | | 相等断言 | `eq: {success: true}` | | 不相等断言 | `ne: {code: 500}` | | 数据库断言 | `db: SELECT COUNT(*) FROM order WHERE id=123` | ------ ### ✅ **6. 数据传递机制** - **extract.yaml**:保存接口提取的值(如订单号、token) - **DebugTalk.get_extract_data()**:在 YAML 中通过 `${}` 动态读取 - **write_extract_yaml()**:提取后写入 `extract.yaml` ------ ### ✅ **7. 日志与异常处理** - **日志记录**:`logger_util.py` → 控制台 + 日志文件(按天轮转) - **异常捕获**:每个模块都 try-catch 并记录日志,确保失败用例不中断整个流程 ------ ### ✅ **8. 报告与通知** - **Allure 报告**:自动生成到 `reports/allures/index.html` - **邮件通知**:`conftest.py` → `pytest_terminal_summary()` 钩子函数收集结果并发送邮件