# ai-pytest **Repository Path**: itestxs/ai-pytest ## Basic Information - **Project Name**: ai-pytest - **Description**: ai-pytest 框架主要参考了httprunner的yaml数据驱动部分设计思路,是基于 Pytest + Pytest-html+ Log + Yaml + Mysql 实现的简易版接口自动化框架。与httprunner不同的是,httprunner是个封装好的工具包,ai-pytest 是半封装的脚本,可以基于这个框架生成pytest技能。 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-03-17 - **Last Updated**: 2026-03-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 一、框架介绍 ## 简介 ai-pytest 框架主要参考了httprunner的yaml数据驱动部分设计思路,是基于 Pytest + Pytest-html+ Log + Yaml + Mysql 实现的简易版接口自动化框架。与httprunner不同的是,httprunner是个封装好的工具包,ai-pytest 是半封装的脚本,目的是让用户自己更容易学习Pytest工具,理解框架设计。 ## 框架理念: 1、一个yaml就是一个接口,包含了接口的请求,断言等信息。 2、脚本执行使用pytest+python代码做逻辑处理,更加方便喜欢写代码的同学 3、让新手同学更加全面的理解pytest框架 在学这个框架前,必备的一点常识是: 1、python基础语法 2、pytest基础包括用例执行,夹具使用等 # 二、实现功能 - yaml数据驱动:实现数据驱动隔离 - 用例标签:在py脚本中可以通过case_tag做用例过滤 - 全局变量池:实现接口之间的关联取值 - 多断言:支持==,!=等多种断言,支持jsonpath的取值方式 - sql数据库断言: 直接在yaml测试用例中写入查询的sql即可断言,无需编写代码,支持多库连接,多个断言 - 自动生成用例代码: 在yaml文件中填写好测试用例, 可以转换为py脚本。 # 三、目录结构 ``` - config ====>> 项目配置文件redis,mysql等 - data ====>> 测试数据文件管理 - logs ====>> 日志记录 - reports ====>> 结果报告,包括html - test_cases ====>> 测试用例 ├── confset.py ====>> 测试夹具 - utils ====>> 各种工具类 ├── assertion ====>> 断言工具 - pytest.ini ====>> pytest配置文件 - conftest.py ====>> 全局夹具配置 - requirements.txt ====>> 相关依赖包文件 - run.py ====>> 执行用例入口文件 ``` # 四、依赖库 ``` requests==2.28.1 jsonpath==0.82 fastapi==0.88.0 pymysql==1.0.2 pyyaml==5.4.1 pytest==7.4.3 pytest-html==4.1.1 py==1.11.0 ``` # 五、启动方式 1、先安装pip install requirements.txt 2、启用utils下的shopping_mock模块 3、运行run.py文件,然后,查看report的结果报告即可。 # 六、使用教程 ## 1、快速开始 ### 1.1、创建用例: 在data目录下创建yaml文件 ==主要的字段格式如下:== **title**:接口名字 **base_url**:域名地址,不填则默认取得setting的BASE_URL,如果填写了,则直接获取填写的 **path**:接口请求地址 **method**:请求方法 **request_data**:统一的请求参数,比如headers **cases**:测试用例集合   **case_name**:测试用例名字,支持多个case_name年编写   **case_tag**:支持参数avl、dis、only 不填则为avl。其中avl就是可用的意思,dis不可用,only是代表只有当前用例生效。如果只传入only,则其他用例则不被执行,优先级是`only>dis>avl`。也可以自定义打tag   **json**:接口的请求体,可以直接输入字典格式(自动生成的用例不是字典格式)。请注意yaml的格式   **params**:接口的url请求参数。(待补充用例)   **assert**:断言,status_code是断言请求状态码。`$.data`是jsonpath的表达式,目前仅支持改表达式写法。目前支持的断言方式请在`assert_type.py`里查看包含,大于、不等于、等于一系列判断 **sql_objects**:sql集合,使用yaml锚点&定义变量,可在yaml中使用*引用变量   **sql_object**:sql 相关参数     **sql**:该case关联的sql,可以将该sql用来做前置还是以及后置     **sql_tag**: 支持数据库tag筛选     **execute_type**: 该字段默认不填为query,支持exec与query两个类型     **extract_sql**:提起该sql的返回内容的某个字段存在变量池中,$.id 也是jsonpath表达式     **assert_sql**:sql的断言,用法同assert     **db_name**: 选择指定哪个数据库连接 ==yaml 模板用例如下:== ``` title: "查询商品" base_url: $config{BASE_URL} path: /items method: GET request_data: headers: Content-Type: application/json token: $global{token} # 使用yaml 锚点&定义变量 sql_objects: &sql_objects - sql_object: &sql_object1 sql: select * from projectInfo where project="tcm" sql_tag: avl # 该字段默认不填为query,支持exec与query两个类型 execute_type: query # 多个sql提取的id变量名需要注意,不要重复 extract_sql: id1: $.id assert_sql: - eq: [ $.id, 50 ] db_name: MYSQL_CONFIG - sql_object: &sql_object2 sql: select * from projectInfo where project="tsf" sql_tag: avl extract_sql: id2: $.id db_name: MYSQL_CONFIG2 cases: - case_name: "搜索-正常" # case_tag 支持参数avl、dis、only 不填则为avl,如果只传入only,则所有case 只会返回only的数据,也可以自定义打tag case_tag: avl assert: - eq: [ status_code, 200 ] - ne: [ $.data, "" ] ref_sql: # 或直接引用整个对象 - *sql_objects - *sql_object1 - *sql_object2 - case_name: "搜索-超出范围" case_tag: avl params: "page=2&limit=10" assert: - eq: [ status_code, 200 ] - eq: [ $.data, [] ] ``` ``` # login.yaml title: "登录" path: /login method: POST request_data: headers: Content-Type: application/json cases: - case_name: "登录-正常" # case_tag 支持参数avl、dis、only 不填则为avl,如果只传入only,则所有case 只会返回only的数据,也可以自定义打tag case_tag: avl json: {"username": "user1", "password": "password1"} extract: token: $.token assert: - eq: [status_code, 200] - ne: [$.token, ""] - case_name: "登录-用户名为空" case_tag: avl json: { "username": "", "password": "password1" } assert: - eq: [status_code, 401] - eq: [$.detail, "Invalid username or password"] ``` ### 1.2、生成py文件 在utils目录下的yaml_to_py文件main修改,yaml_to_pys批量转换整个data文件夹下的yaml文件,yaml_to_py转换指定的yaml文件,参数,cover代表是否覆盖,传入true,则会覆盖你现有的。 ``` if __name__ == '__main__': yaml_to_pys() # yaml_to_py("login.yaml") ``` case_datas 为自动获取测试用例集,可以通过get_case_data(case_tag="tag")中的case_tag去过滤特定标签用例。 ### 1.3、运行脚本 #### 1.3.1 单个脚本运行 每个执行py脚本都可直接右击执行 #### 1.3.2 全部运行 点击运行run文件,可以通过testenv 参数指定获取哪个环境的配置。 ### 1.4 报告查看 如果是单个配置,则直接在当前test_cases目录下就可以看到,如果是run脚本执行,则报告统一放在reports 目前的报告格式是pytest-html。如果要用allure,则可以自己修改使用。 ## 2、功能介绍 ### 2.1、单个接口http请求与断言 response = HttpRequest.simple_request(case_data) Assert(response, case_data.get("assert")).assert_util ``` #!/usr/bin/env python # -*- coding: utf-8 -*- import pytest from utils.assertion.assert_util import Assert from utils.http_request import HttpRequest from utils.read_file_data import ReadFileData class TestLogin(): case_datas = ReadFileData("login.yaml").get_case_data() # get_case_data("tag") 自定义tag输入 @pytest.mark.parametrize('case_data', case_datas, ids=generate_ids(case_datas)) def test_login(self, case_data): response = HttpRequest.simple_request(case_data) Assert(response, case_data.get("assert")).assert_util if __name__ == '__main__': pytest.main(["test_login.py"]) ``` ### 2.2、长链路业务http请求与断言 该功能是添加购车然后付款的流程。 彼此之间有接口依赖问题,通常解决依赖问题有两种 - 第一种:使用框架自带的merge_cases_data函数 test_data = merge_cases_data(add_carts_data, order_pays_data) # 将多个用例合并。merge_cases_data 是依赖接口合并,有两个默认规则,如果两个接口用例数一样多的,如[x,y],[A,B]那用例合并后结果就是[x,A] ,[y,B],如果两个接口用例数不一样,如[x] [A,B]那结果就是[x,A],[x,B],如[x,y] [A]那结果就是[x,A],[y,A]。 - 第二种:自己编写代码逻辑。 每个yaml就是一个接口。获取每个yaml的接口数据,然后获取用例后if else逻辑。如果接口之前有变量依赖,请借助全局变量去取。 ``` import pytest from utils.assertion.assert_util import Assert from utils.http_request import HttpRequest from utils.merge_cases import merge_cases_data, generate_ids from utils.read_file_data import ReadFileData class TestAddPay(): add_carts_data = ReadFileData("add_carts.yaml").get_case_data() # get_case_data("tag") 自定义tag输入 order_pays_data = ReadFileData("order_pays.yaml").get_case_data() test_data = merge_cases_data(add_carts_data, order_pays_data) # 将多个用例合并 print("testdata",test_data) @pytest.mark.parametrize('add_carts_data,order_pays_data', test_data, ids=generate_ids(test_data,"merge")) def test_add_pay(self, add_carts_data, order_pays_data): add_carts_response = HttpRequest.simple_request(add_carts_data) # print(add_carts_response.json()) Assert(add_carts_response, add_carts_data.get("assert")).assert_util order_pays_response = HttpRequest.simple_request(order_pays_data) # print(order_pays_response.json()) Assert(order_pays_response, order_pays_data.get("assert")).assert_util if __name__ == '__main__': pytest.main(["test_add_pay.py"]) ``` ### 2.3、数据库断言   sql_datas = SqlRequest.get_sql_data(case_data) # 支持get_sql_data(sql_tag="tag") 用例按照tag筛选   execute_sql_assert(sql_datas) # 数据库执行以及断言,支持多个数据库多个库的断言,如果断言为空,则会跳过断言 如果要将数据库字段提取出来,则需要修改execute_sql_assert函数逻辑,加上   GlobalVars.update_global_vars(key="data", value=data) # 将sql执行结果添加到公共变量中 ``` import pytest from utils.assertion.assert_util import Assert from utils.http_request import HttpRequest from utils.merge_cases import generate_ids from utils.read_file_data import ReadFileData from utils.sql_reqeust import SqlRequest, execute_sql_assert class TestSearch(): case_datas = ReadFileData("search_items.yaml").get_case_data() # get_case_data(case_tag="tag") 用例按照tag筛选 @pytest.mark.parametrize('case_data', case_datas, ids=generate_ids(case_datas)) def test_search_items(self, case_data): print("case_data", case_data) response = HttpRequest.simple_request(case_data) print(response.json()) Assert(response, case_data.get("assert")).assert_util # demo-数据库断言写法 sql_datas = SqlRequest.get_sql_data(case_data) # get_sql_data(sql_tag="tag") 用例按照tag筛选 execute_sql_assert(sql_datas) # 数据库执行以及断言 if __name__ == '__main__': pytest.main(["test_search_items.py"]) ``` ### 2.4、变量设置 主要的变量有两种,一个是全局变量,主要是接口数据库字段等值的传参使用。一个是配置变量,拿去配置里的信息。 #### 2.4.1、全局变量 使用用法:使用$global{}关键字获取 > $global{token} 以上例子是获取全局变量中token的变量 `注意`:全局变量如果命名重复会导致值被替换,请使用不同的变量名。 #### 2.4.2 系统配置变量 配置变量获取的是config里的setting值,启动的时候,会自动获取当前环境的配置 >$config{BASE_URL} ### 2.5 前后置处理 目前前后置处理由用户自己处理,较为常用的用法是,使用pytest的夹具功能 ### 2.6 环境配置切换 使用testenv参数即可切换生产以及测试环境的配置。默认不填的情况下,使用的是测试环境的配置。配置读取的是settings里的信息 ### 2.7 文件读取 read_file_data提供函数,支持读取json、yaml、csv、txt等文件 ### 2.8 通知 在config settings下配置project、feishu_key信息,运行run脚本,即可发送飞书通知,注意只有运行run脚本才可以发送飞书通知 ![输入图片说明](utils/notify/image.png) # 更多AI测试,可关注 ![输入图片说明](utils/notify/xhs.png) ![输入图片说明](utils/notify/wx.png)