Pytest+requests进行接口自动化测试2.0(yaml)
接口自动化测试学习
- YAML实战演练(登录接口)
- 一. 创建yaml文件
- 1. 创建yaml文件(xxx.yaml)
- 2. 基本语法
- 3. 案例练习与讲解(以登录接口为例)
- 4. PyYAML 库的常用函数
- 二. yaml文件的读取
- 1. 导入第三方库。可通过 pip install PyYAML 下载
- 2. 定义一个接收参数 file(即 YAML 文件的路径)的函数
- 3. with open 打开指定路径的文件,以 只读模式 ('r') 和 UTF-8 编码 打开
- 4. try...except 异常处理
- 1)Exception类异常
- 2)常用 raise 的异常类型
- 5. 主程序入口,往函数中传入login.yaml文件
- 三. yaml文件的写入(指定文件)
- 1. 封装一个ReadYamlData 类:读取 YAML 文件
- os模块简单介绍 (os.path 子模块(重要!))
- 2. 封装一个WriteYamlData 类:写入另一个YAML 文件
- 3. 主函数运行
- json序列化和反序列化(重要!!!)
- 四. yaml文件的使用
- 1. 从 YAML 文件读取测试用例,并发送 HTTP 请求示例
- 2. 示例解析
- 五. yaml测试用例参数动态读取
- 1. 常见动态参数类型与写法
- 1)环境变量注入(多环境支持)
- 2)前置接口依赖提取(extract,常用于获取 token、user_id)
- 3)函数调用型占位符(最灵活)
- 4)加密字段动态处理(如 MD5、Base64)
- 5)变量引用与复用(类似全局变量)
- 6)数据参数化( 自动生成多条测试用例,每组数据执行一次)
- 7)条件判断与跳过(高级)
- 2. 案例:动态参数 token 的替换
- 1)创建 yaml 接口文档 login.yaml,将 token 设为动态参数
- 2)将读取的接口返回数据写入指定的 extract.yaml 文件(参考 : 三、yaml文件的写入)
- 3)从 extract.yaml 读取接口返回数据的变量值 value
- 4)创建 debugtalk.py 文件,读取特定的变量值 value 并自定义(举例:password 的 value 为列表)
- 5)替换 login.yaml 中的动态参数 token
YAML实战演练(登录接口)
YAML 是一种用来 存储和传输结构化数据 的文件格式,常用于配置文件、测试用例、项目设置等
一. 创建yaml文件
1. 创建yaml文件(xxx.yaml)
2. 基本语法
介绍 YAML 的基本语法,如键值对、列表、嵌套结构等
写法 | 含义 |
---|---|
key: value | 键值对(注意冒号后有空格) |
# 这是注释 | 注释 |
list: - item1 - item2 | 列表 |
dict: name: 张三 age: 25 | 字典 |
data: {name: 张三, age: 25} | 内联写法 |
long_str: > 这是一段 换行的文字 | 多行字符串 |
- 示例代码(基本语法)
api_name: 登录接口 # 键值对写法# 注释
test2: # 列表写法- data1- data2test_case: # 嵌套字典写法case1: 有效登录case2: 无效登录test: {name: liu,age: 18} # 内联写法long_str: > # 多行字符串写法这是一段换行的文字
3. 案例练习与讲解(以登录接口为例)
- baseInfo:api_name: 用户登录url: /dar/user/login --- 这里是相对路径,通常会拼接 base_urlmethod: Postheader:Content-Type: application/x-www-form-urlencoded;charset=UTF-8test_case:- case_name: 用户名和密码正确登录验证data: --- 根据请求头设置data,jsonuser_name: test01password: admin123validation:- eq: [msg, 登录成功]extract: --- 从响应 JSON 中提取 token 字段,保存为变量 tokentoken: $.token --- JSONPath 语法,表示根节点下的 token 字段- case_name: 用户名和密码错误登录验证data: user_name: test0123password: admin123validation:- eq: [msg, 登录成功] --- 自动化框架会解析这个 validation,自动验证extract:token: $.token
- 开头的短横线:设计成列表是为了以后可以添加多个接口
- baseInfo: { ... } # 登录
- baseInfo: { ... } # 获取用户信息
- test_case:这个接口的多个测试用例
test_case:- case_name: ...- case_name: ...
- validation:断言(验证响应是否符合预期)
validation:- eq: [msg, 登录成功] --- 自动化框架会解析这个 validation,自动验证
- extract:从响应中提取变量(用于后续接口调用)
extract: --- 从响应 JSON 中提取 token 字段,保存为变量 tokentoken: $.token --- JSONPath 语法,表示根节点下的 token 字段
4. PyYAML 库的常用函数
函数 | 用途 |
---|---|
yaml.safe_load() ,yaml.safe_load_all() | 读取单个 (多个) YAML 文档 |
yaml.dump(),yaml.dump_all() | 写入单个 (多个) YAML 文档 |
yaml.add_representer() | 高级用法:自定义序列化 |
二. yaml文件的读取
- 读取yaml文件,并将其转换为python可解析的内容
import yamldef get_testcase_yaml(file):"""获取yaml文件的数据:param file: yaml文件的路径:return: 返回读取到的yaml文件"""try:with open(file, 'r', encoding='utf-8') as f:yaml_date = yaml.safe_load(f)return yaml_dateexcept Exception as e:raise eif __name__ == '__main__':res = get_testcase_yaml('login.yaml')print(res)
- 语法逐个解释
1. 导入第三方库。可通过 pip install PyYAML 下载
import yaml
2. 定义一个接收参数 file(即 YAML 文件的路径)的函数
def get_testcase_yaml(file):"""获取yaml文件的数据(获取测试用例的 YAML 数据):param file: yaml文件的路径:return: 返回读取到的yaml文件"""
可以用于多个 YAML 文件的读取
3. with open 打开指定路径的文件,以 只读模式 (‘r’) 和 UTF-8 编码 打开
使用 with 的好处: 自动关闭文件,即使发生异常也不会造成资源泄漏
with open(file, 'r', encoding='utf-8') as f:yaml_date = yaml.safe_load(f)return yaml_date
- yaml.safe_load( f ) 将打开的文件对象 f 安全解析成 Python 的数据结构(如字典、列表等)
- return yaml_date 把解析后的数据返回出去,供后续使用(比如执行测试用例)
4. try…except 异常处理
使用 try…except,如果文件路径错了或内容非法,程序不会报错退出,而是打印错误信息并继续运行
try:...
except Exception as e:raise e
代码部分 | 含义 |
---|---|
except | 当 try 块中出现异常时,就跳到这里处理 |
Exception | 捕获所有继承自 Exception 的异常(即绝大多数常见错误) |
as e | 把异常对象保存到变量 e 中,方便查看具体错误信息 |
raise e | raise 是 Python 中的一个关键字,作用是 “主动抛出一个异常” |
1)Exception类异常
Exception 是一个“父类”,它能捕获几乎所有你常见的、程序运行时出错的情况
如果想要精细化 Exception 异常处理,可以捕获具体的异常(生产环境)
- 示例:
try:with open(file, 'r', encoding='utf-8') as f:return yaml.safe_load(f)
except FileNotFoundError:print(f"文件未找到: {file}")
except yaml.YAMLError as e:print(f"YAML解析错误: {e}")
except PermissionError:print(f"没有权限读取文件: {file}")
except Exception as e:print(f"未知错误: {e}")
return None
2)常用 raise 的异常类型
异常类型 | 适用场景 |
---|---|
ValueError | 值不合适,如年龄为负数 |
TypeError | 类型错误,如传了字符串要当数字用 |
FileNotFoundError | 文件不存在 |
KeyError | 字典中找不到某个键 |
PermissionError | 没有权限访问文件或目录 |
Exception | 通用异常(不推荐滥用) |
5. 主程序入口,往函数中传入login.yaml文件
if __name__ == '__main__':res = get_testcase_yaml('login.yaml')print(res)
三. yaml文件的写入(指定文件)
将读取的接口数据写入指定的 yaml 文件
- 把写入拆分为读取 + 写入
1. 封装一个ReadYamlData 类:读取 YAML 文件
- 整体展示
import os
import yamlclass ReadYamlData:"""读取 YAML 数据"""def __init__(self, yaml_file='login.yaml'):self.yaml_file = yaml_filedef read(self):"""读取 YAML 文件并返回字典"""if not os.path.exists(self.yaml_file):raise FileNotFoundError(f"配置文件不存在: {self.yaml_file}")with open(self.yaml_file, 'r', encoding='utf-8') as f:try:return yaml.safe_load(f) or {}except yaml.YAMLError as e:raise ValueError(f"YAML 解析错误: {e}")
- 解释分析
class ReadYamlData:"""读取 YAML 数据"""def __init__(self, yaml_file='login.yaml'):self.yaml_file = yaml_file
- 定义了一个类 ReadYamlData,用于封装读取 YAML 文件的功能。
- 构造函数接受一个可选参数 yaml_file,默认值为 ‘login.yaml’,表示如果调用时不传路径,则默认读取当前目录下的 login.yaml 文件。
- 将文件名保存在实例变量 self.yaml_file 中,供后续方法使用。
def read(self):"""读取 YAML 文件并返回字典"""if not os.path.exists(self.yaml_file):raise FileNotFoundError(f"配置文件不存在: {self.yaml_file}")with open(self.yaml_file, 'r', encoding='utf-8') as f:try:return yaml.safe_load(f) or {}except yaml.YAMLError as e:raise ValueError(f"YAML 解析错误: {e}")
- 使用 os.path.exists() 检查文件是否存在
- return xx or {}:如果文件为空或解析结果为 None,返回空字典 {},避免返回 None 导致后续 KeyError。
os模块简单介绍 (os.path 子模块(重要!))
os 是 Python 的标准库模块之一,全称是 “Operating System”(操作系统)。它提供了与操作系统进行交互的接口,掌握它就等于掌握了文件系统操作的基本能力
总结模块使用要点 | 说明 |
---|---|
使用 os.path.join() 拼接路径 | 保证跨平台兼容 |
使用 os.path.exists() 判断文件前先检查 | 避免 FileNotFoundError |
优先使用 os.makedirs(…, exist_ok=True) | 安全创建多级目录 |
读写文件时配合 encoding=‘utf-8’ | 支持中文 |
避免直接拼接路径字符串 | 如 ‘folder\file.txt’ 易出错 |
os.path 是 os 模块中专门处理路径的子模块,非常常用:
函数 | 作用 |
---|---|
os.path.exists(path) | 路径是否存在 |
os.path.isfile(path) | 是否是文件 |
os.path.isdir(path) | 是否是目录 |
os.path.join(a, b, …) | 安全拼接路径 |
os.path.basename(path) | 获取文件名(如 ‘file.txt’) |
os.path.dirname(path) | 获取目录部分,例如:dirname(‘output/data.yaml’) → ‘output’ |
os.path.splitext(path) | 分离文件名和扩展名 |
2. 封装一个WriteYamlData 类:写入另一个YAML 文件
- 整体展示
class WriteYamlData:"""写入 YAML 数据"""def __init__(self, yaml_file='extract.yaml'):self.yaml_file = yaml_filedef write(self, data):"""写入数据到 YAML 文件(覆盖模式):param data: dict 类型数据"""if not isinstance(data, dict):raise TypeError("写入的数据必须是字典类型")# 确保目录存在(如果路径含子目录)parent_dir = os.path.dirname(self.yaml_file)if parent_dir and not os.path.exists(parent_dir):os.makedirs(parent_dir)# 写入文件(覆盖原内容)with open(self.yaml_file, 'w', encoding='utf-8') as f:yaml.dump(data, f, allow_unicode=True, sort_keys=False, default_flow_style=False)
- 解释分析
class WriteYamlData:"""写入 YAML 数据"""def __init__(self, yaml_file='extract.yaml'):self.yaml_file = yaml_file
- yaml_file=‘extract.yaml’:设置默认参数,若不指定文件名,则默认写入 extract.yaml文件(不会自动创建文件)
def write(self, data):"""写入数据到 YAML 文件(覆盖模式):param data: dict 类型数据"""if not isinstance(data, dict):raise TypeError("写入的数据必须是字典类型")
isinstance(obj, type):判断 obj 是否是某个类型(这里是 dict)
parent_dir = os.path.dirname(self.yaml_file)if parent_dir and not os.path.exists(parent_dir):os.makedirs(parent_dir)
- os.path.dirname(获取路径) 和 os.makedirs(创建路径) —— 确保你要写入的 YAML 文件所在的目录存在,如果不存在,就自动创建它
- if parent_dir:防止根目录或当前目录(如 ‘’)被误判
with open(self.yaml_file, 'w', encoding='utf-8') as f:yaml.dump(data, f, allow_unicode=True, sort_keys=False, default_flow_style=False)
- with open(…, ‘w’, encoding=‘utf-8’) as f: —— 安全写入文件('w’为覆盖 'a’为底部追加)
- yaml.dump(…) —— 序列化为 YAML 格式
3. 主函数运行
从 YAML 文件读取登录接口的测试数据 → 发送登录请求 → 提取返回的 token → 把 token 写入另一个 YAML 文件供后续使用
调用ReadYamlData()类中的read方法,解析‘login.yaml’文件,在解析好的数据中获得url,data,header
if __name__ == '__main__':read_yaml = ReadYamlData()res = read_yaml.read()[0]url = res['baseInfo']['url']new_url = 'http://192.168.4.200' + urldata = res['test_case'][0]['json']header = res['baseInfo']['header'] ------ 拆分yaml文件
发送post接口请求,并拿到返回数据中的 token (注: 返回的对象需用 .json()转换为json格式)
resp = requests.post(url=new_url, json=data, headers=header) ----- 调用requests.posttoken = resp.json()['data']['token'] ------ 获取接口返回数据中的token
创建一个字典,把提取到的 token 存进去,准备写入文件
write_date = {} ------ 创建write_date,将获取到的token传入write_date['TOKEN'] = token
调用WriteYamlData() 类中的write方法,把 {‘TOKEN’: ‘xxx’} 写入 extract.yaml 文件
read_data = WriteYamlData() read_data.write(write_data) # 传入字典类型
- 生成结果
json序列化和反序列化(重要!!!)
方法 | 输入类型 | 输出类型 | 用途 |
---|---|---|---|
json.loads(str) | 字符串 | 字典(dict) | 反序列化:字符串 → 字典 |
json.dumps(dict) | 字典 | 字符串 | 序列化:字典 → 字符串 |
json序列化,其实就是将python的宇典类型转换为字符串类型
- (ensure_ascii=False 让中文正常显示)
json_str = json.dumps(resp,ensure_ascii=False)print(json_str)print(type(json_str))
json反序列化,其实就是将宇符串类型转换为字典类型
json_dict = json.loads(json_str)print(json_dict)print(type(json_dict))
四. yaml文件的使用
- 前置条件,yaml文件内容
- baseInfo:api_name: 用户登录url: /dar/user/loginmethod: Postheader:Content-Type: application/x-www-form-urlencoded;charset=UTF-8test_case:- case_name: 用户名和密码正确登录验证data: # 根据请求头设置data,jsonuser_name: test01password: admin123validation:- eq: [msg, 登录成功]extract:token: $.token
- 封装了 requests 库的模块 sendrequests.py
1. 从 YAML 文件读取测试用例,并发送 HTTP 请求示例
import yaml
def run_yaml(file):try:with open(file, 'r', encoding='utf-8') as f:yaml_data = yaml.safe_load(f)return yaml_dataexcept Exception as e:print(e)if __name__ == '__main__':res = run_yaml('login.yaml')[0] # 读取YAML中的第一个测试用例url = res['baseInfo']['url'] # 获取原始URL路径new_url = 'http://127.0.0.1:8787' + url ---- 拼接成完整URLmethod = res['baseInfo']['method'] # 获取请求方法(如POST)data = res['test_case'][0]['data'] # 获取第一组测试数据header = res['baseInfo']['header'] # 获取请求头from sendrequests import SendRequest ---- 导入自定义请求类send = SendRequest() # 创建请求对象result = send.run_main(method=method, url=new_url, data=data, header=header) # 发送请求print(result) # 打印响应结果
2. 示例解析
- res = run_yaml(‘login.yaml’)[0]
调用一个叫 run_yaml 的函数,读取 login.yaml 文件,run_yaml() 返回的是一个列表,读取该列表的第一个用例
- url = res[‘baseInfo’][‘url’]
字典形式的数据读取方式
五. yaml测试用例参数动态读取
- 为什么要进行参数动态读取 ?
(热加载)在接口测试中,某些参数不能写死,需要通过动态解析实现参数值的替换。如:不同的环境配置, token、时间戳、随机数、加密字段
1. 常见动态参数类型与写法
1)环境变量注入(多环境支持)
- 前置条件数据
# config/test.yaml
env: test
base_url: http://test.api.com
db_host: 192.168.10.20# config/prod.yaml
env: prod
base_url: https://api.company.com
db_host: 10.10.10.100
- 后续测试用例中引用
- case_name: 用户登录url: "{{ENV:base_url}}/login"
2)前置接口依赖提取(extract,常用于获取 token、user_id)
- 前置条件数据
- case_name: 登录获取 tokenextract:token: $.data.token # 使用 JSONPath 提取user_id: $.data.user.id
- 后续测试用例中引用
- case_name: 获取用户信息request:method: GETurl: /user/{{token}}headers:Authorization: "Bearer {{token}}"
3)函数调用型占位符(最灵活)
支持自定义函数:UUID()、timestamp()、random_phone()、md5(str) 等
- case_name: 创建订单json:order_no: "{{UUID}}" --- 生成唯一订单号timestamp: "{{TIMESTAMP}}" --- 当前时间戳phone: "{{RANDOM_PHONE}}" --- 随机手机号email: "{{RANDOM_EMAIL}}" --- 随机邮箱sign: "{{MD5:{{timestamp}}key}}" --- 动态签名(支持嵌套)
4)加密字段动态处理(如 MD5、Base64)
- 简单版(直接加密)
json:password: "{{md5:123456}}" --- 明文自动 MD5data: "{{base64:hello world}}" --- `在这里插入代码片`Base64 编码
- 复杂版(引用变量 + 加密)
json:password: "{{sha256:{{PWD}}}}" --- 引用变量 + 加密
5)变量引用与复用(类似全局变量)
test_cases:- name: 登录request:url: "{{api_prefix}}/login"json:user_name: "{{username}}"password: "{{md5:{{PWD}}}"
6)数据参数化( 自动生成多条测试用例,每组数据执行一次)
- name: 多组登录测试parameters:user_info:- ["admin", "123456"]- ["guest", "guest123"]- ["invalid", "wrong"]request:url: /loginmethod: POSTjson:username: "{user_info[0]}"password: "{user_info[1]}"
7)条件判断与跳过(高级)
- name: 只有登录成功才执行request:url: /delete/userskip_if:condition: "{{token}}" is null # 如果 token 为空则跳过reason: "未登录,跳过删除操作"
- 总结
写法 | 示例 | 用途 |
---|---|---|
{{var}} | {{token}} | 引用变量 |
{{ENV:key}} | {{ENV:base_url}} | 环境变量 |
{{func:arg}} | {{md5:123}} | 函数处理 |
{param} | {user_id} | 参数化 |
$var | $token | 简写(部分框架) |
$.json.path | $.data.token | JSONPath 提取 |
2. 案例:动态参数 token 的替换
{{get_data(TOKEN)}} 类型的动态参数均可使用(支持同一yaml文档多个)
1)创建 yaml 接口文档 login.yaml,将 token 设为动态参数
header:Content-Type: application/jsonTOKEN: "{{get_data(TOKEN)}}"
2)将读取的接口返回数据写入指定的 extract.yaml 文件(参考 : 三、yaml文件的写入)
class WriteYamlData:"""写入 YAML 数据"""def __init__(self, yaml_file='extract.yaml'):self.yaml_file = yaml_filedef write(self, data):"""写入数据到 YAML 文件(覆盖模式):param data: dict 类型数据"""if not isinstance(data, dict):raise TypeError("写入的数据必须是字典类型")# 确保目录存在(如果路径含子目录)parent_dir = os.path.dirname(self.yaml_file)if parent_dir and not os.path.exists(parent_dir):os.makedirs(parent_dir)# 写入文件(覆盖原内容)with open(self.yaml_file, 'w', encoding='utf-8') as f:yaml.dump(data, f, allow_unicode=True, sort_keys=False, default_flow_style=False)
3)从 extract.yaml 读取接口返回数据的变量值 value
读取extract.yaml文件数据 - - - > 如果没有extract.yaml文件,则创建空文件返回None,如果有extract.yaml文件直接读取传入 key(node_name) 的变量值 value
class GetExtractData:"""读取接口提取的变量值"""def __init__(self, yaml_file='extract.yaml'):self.yaml_file = yaml_filedef get_extract_data(self,node_name):"""获取extract.yaml文件的数据:param node_name: extract.yaml文件中的key值:return:"""if not os.path.exists(self.yaml_file): --- 如果不存在extract.yaml,创建空文件print(f'{self.yaml_file}不存在,正在创建...')with open(self.yaml_file, 'w', encoding='utf-8') as f:yaml.dump({}, f, allow_unicode=True, sort_keys=False, default_flow_style=False)return Nonewith open(self.yaml_file, 'r', encoding='utf-8') as rf:open_data = yaml.safe_load(rf)return open_data.get(node_name)
4)创建 debugtalk.py 文件,读取特定的变量值 value 并自定义(举例:password 的 value 为列表)
自动化测试框架中的 debugtalk.py 文件,用于在 YAML 测试用例中调用自定义函数(如动态参数、加密、数据提取等)
- 整体展示
实现从 extract.yaml 文件中读取之前接口提取的变量(如 token、password等),并支持多种读取方式:
- 直接获取
- 随机取一个
- 取第 N 个
- 拼接成字符串等
class DebugTalk:def __init__(self):self.extract_data = GetExtractData()def get_extract_order_data(self,data,randoms):if randoms not in [0,-1,-2]:return data[randoms - 1]def get_data(self,node_name,randoms=None):"""获取 extract.yaml 中的数据,支持多种读取方式:param node_name: 键名,如 'TOKEN':param randoms: 控制读取方式None: 原样返回1,2,3...: 返回第 N 个(从1开始)0: 随机一个-1: 拼接成字符串:return:"""token_datas = self.extract_data.get_extract_data(node_name)if randoms is not None:intrandoms = int(randoms)data_value = {randoms :self.get_extract_order_data(token_datas,randoms),0 : random.choice(token_datas), # random.choice从数组里面随机读取-1 : ''.join(token_datas), # 拼接字符串-2 : ','.join(token_datas).split(',') # 转换为数组}token_datas = data_value[intrandoms]return token_datasif __name__ == '__main__':debug_talk = DebugTalk()print(debug_talk.get_data('TOKEN'))print(debug_talk.get_data('password',3))
- 代码详解
创建一个可以被 YAML 测试用例调用的“工具类”。使用了上面的 3)GetExtractData 来读数据。
class DebugTalk:def __init__(self):self.extract_data = GetExtractData()
若数据为列表,且传入的randoms不为0,-1,-2,则取 data[randoms - 1] (假如传入3,则取索引为2的数据,这里没有设置边界值)
def get_extract_order_data(self,data,randoms):if randoms not in [0,-1,-2]:return data[randoms - 1]
通过 GetExtractData().get_extract_data 从 extract.yaml 中拿到数据
def get_data(self,node_name,randoms=None):token_datas = self.extract_data.get_extract_data(node_name)
判断取得的数据是否需要特殊处理(randoms 不为空)randoms为空不需要处理
- 根据 randoms 的值选择不同的返回方式:
None: 原样返回
1,2,3…: 返回第 N 个(调用get_extract_order_data,从索引0开始)
0: 随机一个
-1: 拼接成字符串
-2:转换为数组
if randoms is not None:intrandoms = int(randoms)data_value = {randoms :self.get_extract_order_data(token_datas,randoms),0 : random.choice(token_datas), # random.choice从数组里面随机读取-1 : ''.join(token_datas), # 拼接字符串-2 : ','.join(token_datas).split(',') # 转换为数组}token_datas = data_value[intrandoms]return token_datas
调用DebugTalk类里的 get_data 方法,打印我们需要的数据
if __name__ == '__main__':debug_talk = DebugTalk()print(debug_talk.get_data('TOKEN'))print(debug_talk.get_data('password',3))
5)替换 login.yaml 中的动态参数 token
- 整体展示
在自动化测试框架中解析并替换类似 {{ func_name(param) }} 格式的表达式
from run_yaml import ReadYamlData
from debugtalk import DebugTalk
import jsonclass BaseRequest:def __init__(self):self.read = ReadYamlData()def replace_load(self,data):"""yaml文件替换解析有{{ }}格式的数据"""str_data = dataif not isinstance(data,str):str_data = json.dumps(data,ensure_ascii=False)print('yaml文件替换解析前',str_data)start = 0while True:s = str_data.find('{{', start) # 从str_data第 0 个位置开始找if s == -1: breakb = str_data.find('(', s) # 从str_data第 s 个位置开始找 (if b == -1: breake = str_data.find(')', b) # 从str_data第 b 个位置开始找 )if e == -1: breakfunc_name = str_data[s+2:b] # 取出函数名, s 为{{get_data(TOKEN)}}第一个{的位置func_params = str_data[b+1:e] # 取出参数名, b 为{{get_data(TOKEN)}}第一个( 的位置func_all = str_data[s:e + 3] # 全部需替换内容extract_data = getattr(DebugTalk(),func_name)(*func_params.split(',') if func_params else "")str_data = str_data.replace(func_all,extract_data)print('yaml文件替换解析后',str_data)continueif data and isinstance(data,dict):data = json.loads(str_data)else:data = str_datareturn dataif __name__ == '__main__':data = ReadYamlData().read()[0]base = BaseRequest()base.replace_load(data)
- 解析说明
- ReadYamlData: 用于读取 login.yaml 数据(参考 : 三、yaml文件的写入)
- 调用 debugtalk.py 文件中的 DebugTalk 类
from run_yaml import ReadYamlData
from debugtalk import DebugTalk
import json
核心方法:replace_load(data)
- 该方法负责查找并替换数据中所有形如 {{ func_name(param) }} 的表达式。
- json.dumps(data,ensure_ascii=False) 将数据全转换为字符串类型,方便后续用字符串方法 find 查找 {{ }}
class BaseRequest:def __init__(self):self.read = ReadYamlData()def replace_load(self,data):"""yaml文件替换解析有{{ }}格式的数据"""str_data = dataif not isinstance(data,str):str_data = json.dumps(data,ensure_ascii=False) print('yaml文件替换解析前',str_data)
循环查找 {{ func(…) }} 取出函数名 get_data,参数 TOKEN ,和需替换的内容
start = 0while True:s = str_data.find('{{', start) # 从str_data第 0 个位置开始找if s == -1: breakb = str_data.find('(', s) # 从str_data第 s 个位置开始找 (if b == -1: breake = str_data.find(')', b) # 从str_data第 b 个位置开始找 )if e == -1: breakfunc_name = str_data[s+2:b] # 取出函数名, s 为{{get_data(TOKEN)}}第一个{的位置func_params = str_data[b+1:e] # 取出参数名, b 为{{get_data(TOKEN)}}第一个( 的位置func_all = str_data[s:e + 3] # 全部需替换内容
动态调用 DebugTalk 中的函数获取token值,替换并更新字符串
- getattr(DebugTalk(), func_name) 获取 DebugTalk 类中名为 func_name 的方法。
- func_params.split(‘,’) 将参数按逗号分割成列表。
- ( * ) 解包参数传给函数执行,如果无参数,则传空字符串。
extract_data = getattr(DebugTalk(),func_name)(*func_params.split(',') if func_params else "")str_data = str_data.replace(func_all,extract_data)print('yaml文件替换解析后',str_data)continue
最后:还原数据类型
- 如果原始输入是字典,则尝试把最终字符串再转回字典,否则直接返回字符串
if data and isinstance(data,dict):data = json.loads(str_data)else:data = str_datareturn data