API 接口自动化测试详细图文教程学习系列24--如何用Pytest去设计接口测试用例并执行 测试学习记录仅供参考如何用Pytest去设计接口测试用例优化代码添加前后置操作处理防止重复写入数据1、先删除 testcase 软件包的 login 和 userManager 目录中的测试文件2、在 login 目录下新建 test_login.py 文件并输入以下内容# 导包--引入pytest模块框架 import pytest # 导入 读取 yaml 文件数据模块--读取文件模块 from unit_tools.handle_data.yaml_handler import read_yaml, write_yaml # 导入读取配置文件模块--解析配置文件 from unit_tools.handle_data.configParse import ConfigParser # 引入 发起接口请求 模块 from unit_tools.sendrequests import SendRequests # 在最外层先 实例化一个对象--读取配置文件 conf ConfigParser() # 创建一个测试类 class TestLogin: 登录模块 # 参数化--通过参数化去读取到接口信息 pytest.mark.parametrize(api_info, read_yaml(./datas/login.yaml)) # 创建一个测试用例 def test_login_module(self, api_info): # 从yaml文件获取接口信息 # conf.get_host(host) 拿到服务器地址--再返回赋值给conf_url conf_url conf.get_host(host) url conf_url api_info[baseInfo][url] method api_info[baseInfo][method] # 这里登录接口做特殊处理了--目前不需要请求头所以暂时传空 header None # 入参 req_param api_info[testCase][0][data] # 调用接口SendRequests类去执行接口请求 # 先实例化这个类--然后给它返回一个对象 send SendRequests() # 再通过这个对象去调用 执行接口请求 方法--里面开始传参 response send.execute_api_request(api_nameNone, urlurl, methodmethod, headerheader, case_nameNone, cookieNone, fileNone, datareq_param) # 将接口返回值转换为json格式 result_json response.json() print(f接口实际返回信息{result_json}) # 把登录接口的返回值token写入到extract.yaml文件中 login_token {} login_token[token] result_json[token] write_yaml(login_token) # 结果断言 assert result_json[msg] 登录成功, 登录接口测试失败!3、执行主函数运行 run.py 文件查看执行结果不尽理想多次执行后会重复出现写入数据继续下一步4、修改 datas 目录下 adduser.yaml 文件内容- baseInfo: api_name: 用户新增 url: /dar/user/addUser method: post header: ${get_headers(data)} testCase: - case_name: 用户新增正常校验 data: username: testadduser password: tset6789890 role_id: 123456789 dates: 2000-12-31 phone: 13800000000 token: ${get_extract_data(token)}5、在项目根目录下新建 conftest.py 文件并输入以下内容增加前后置操作在测试用例执行前把 extract.yaml 文件给清空# 导包 import pytest from unit_tools.handle_data.yaml_handler import clear_yaml pytest.fixture(scopesession, autouseTrue) def clear_extract(): clear_yaml()6、在userManager 目录下新建 test_user_manager.py 文件并输入以下内容# 导包 import pytest from unit_tools.handle_data.yaml_handler import read_yaml from unit_tools.handle_data.configParse import ConfigParser from unit_tools.sendrequests import SendRequests # 引入 动态解析 模块 from unit_tools.apiutils import RequestsBase conf ConfigParser() # 也做个实例化 parse RequestsBase() class TestUserManager: 用户管理模块 pytest.mark.parametrize(api_info, read_yaml(./datas/adduser.yaml)) def test_user_add(self, api_info): # 解析yaml文件里面的${......},得到一个调用后的接口信息 api_info parse.parse_and_replace_variables(api_info) # 从解析后的接口信息中获取接口信息 conf_url conf.get_host(host) url conf_url api_info[baseInfo][url] method api_info[baseInfo][method] # header eval(api_info[baseInfo][header]) header None req_param api_info[testCase][0][data] # 调用接口SendRequests类去执行接口请求 send SendRequests() # 再通过这个对象去调用 执行接口请求 方法--里面开始传参 response send.execute_api_request(api_nameNone, urlurl, methodmethod, headerheader, case_nameNone, cookieNone, fileNone, datareq_param) # 将接口返回值转换为json格式 result_json response.json() print(f接口实际返回信息{result_json}) # 结果断言 assert result_json[msg] 新增成功, 新增接口测试失败!7、再执行主函数运行 run.py 文件多次重复执行后不会出现重复数据信息了约定 yaml 文件接口信息标准格式8、接口信息的yaml文件格式约定例如login.yaml文件- baseInfo: api_name: 用户登录 url: /dar/user/login method: post header: Content-Type: application/x-www-formurlencoded;charsetUTF-8 cookies: Cookie: test123456789020241011 testCase: - case_name: 用户正常登录校验 data: user_name: test01 passwd: admin123 files: file: ./data/login.yaml validation: - contain: {msg:登录成功} - eq: {msg:登录成功} extract: token: $.token extract_list: goodid: $.good_id文件格式约定关键词内容说明yaml文件必须包含 baseInfo和testCase 最外面 “- ”一个横杠表示数据是一个list类型baseInfo下面的关键词api_name : 接口名称url : 接口地址method : 请求方式header (可选) : 请求头根据实际接口 决定是否填写cookies (可选) : cookie根据实际项目情况决定testCase下面的关键词case_name : 测试用例名称data/params/json可选 : 请求参数数据类型根据接口请求头决定写什么类型一般情况下get请求就写params如果是post请求这时要根据请求头是表单提交就写data若是json格式提交就填写jsonfiles可选文件上传接口validation : 断言结果有多种断言模式如包含断言 contain、相等断言 eq、不相等断言 nq、数据库断言 dbextract (可选) : 提取单个值接口的返回值提取存放在extract.yaml 全局文件给别的接口使用达到一个接口之间的上下游参数传递的目的extract_list (可选) : 提取多个值接口的返回值提取存放在extract.yaml给别的接口使用达到一个接口之间的上下游参数传递的目的封装请求接口9、这里写在了项目根目录 unit_tools 软件包下的 apiutils.py 文件中修改 apiutils.py 文件内容# 导包 import json from unit_tools.handle_data.yaml_handler import read_yaml import re from unit_tools.debugtalk import DebugTalk # 引入 配置文件模块 from unit_tools.handle_data.configParse import ConfigParser # 创建一个类 RequestsBase class RequestsBase: # 在RequestsBase类的里面写一个初始化构造函数 def __init__(self): # 实例化一下实例化之后就可以通过这个对象去获取 host self.conf ConfigParser() # 创建一个方法 parse_and_replace_variables解析并且替换变量--传一个参数 yml_data--要解析的yaml文件的数组 def parse_and_replace_variables(self, yml_data): 解析并替换YAML文件数据中的变量引用如${get_extract_data(goodsId,1)} :param yml_data: 解析的YAML数据 :return: 最终要返回的是dict类型--才能给后续的一些方法调用时使用 # if isinstance(yml_data, str)--判断传进来的数据是什么类型--后续需要字符串才能做操作的所以这里先判断是不是字符串格式 # yml_data若是字符串则执行 if 语句左边的代码 直接返回yml_data # yml_data不是字符串--调用json.dumps(yml_data)模块转成字符串--ensure_asciiFalse 参数是可以处理中文 # if else 语句写在一行是 三元运算--通过这一系列之后最终返回一个 新的变量yaml_data_str --它是一个字符串 yml_data_str yml_data if isinstance(yml_data, str) else json.dumps(yml_data, ensure_asciiFalse) # 打印查看 yaml_data_str print(f解析前{yml_data_str}) # 用for循环去判断 字符串yml_data_str里面这种标识 ${ 格式出现的次数--加一个_下划线表示不需要用到这个变量是一个占位符的意思 for _ in range(yml_data_str.count(${)): # if判断这个标识${ 它包含在字符串yml_data_str里面--并且另一半它 } 也包含在字符串yml_data_str里面 if ${ in yml_data_str and } in yml_data_str: # 通过获取到 ${ 标识的起始位置--通过字符串 yml_data_str 的索引 index--获取它的索引的起始位置 strat_index yml_data_str.index(${) # 获取结束位置 标识 }--再加一个参数 它的开头 strat_index end_index yml_data_str.index(}, strat_index) # 拿到开始、结束位置后--通过字符串切片--起始位置、结束位置1--为什么1--因为python中索引切片是左闭右开的一个规则 # 什么是左闭右开--开始位置是包含在切片里面的但是结束元素不包含在切片中所以得1 variable_data yml_data_str[strat_index:end_index 1] # print(f函数名{variable_data}) # 使用正则表达式提取函数名和参数 # 导入re模块--调用re模块中的match方法--里面跟两个参数第一个参数是正则表达式第二个参数是要提取正则表达式的字符串 match re.match(r\$\{(\w)\((.*?)\)\}, variable_data) # 判断一下有没有匹配到--匹配的对象是不是成功了--成功 if match: # 通过match.groups()去拿到函数名还有参数--定义两个变量去接收,第一个函数名func_name、第二个函数值func_params func_name, func_params match.groups() # 先判断它存在--走if左边的代码--给它做一个切片若参数值不存在则返回一个空列表即可 # 参数值有一个或多个--所以需要做一个处理--参数值func_params通过逗号去做一个切片 # 最后再把值重新赋值给它自己 func_params func_params.split(,) if func_params else [] # print(f函数值{func_name}) # 使用面向对象反射getattr调用函数 # 引如类后直接 实例化类--第二个参数传的是一个函数名 func_name--函数调用 (*func_params)--通过星号把参数值func_params去解包传递给参数func_name extract_data getattr(DebugTalk(), func_name)(*func_params) print(f提取到的结果:{extract_data}) # 使用正则表达式替换原始字符串中的变量引用为调用后的结果 # 使用正则表达式替换函数re.sub--第一个参数就是需要替换的哪个参数值第二个参数是解析后得到的数据extract_data第三个参数是原始解析前的yml_data_str yml_data_str re.sub(re.escape(variable_data), str(extract_data), yml_data_str) # 还原数据将其转换为字典类型 # 添加异常处理--像这种转换的最好是加一个异常处理 try: # 直接调用 son.loads(yml_data_str)--把字符串转换成字典 data json.loads(yml_data_str) # json转码异常 except json.JSONDecodeError: # 返回原始数据yml_data_str--赋值给新变量 data data yml_data_str return data # 写个方法 execute_test_cases--传一个参数 接口的信息--即写yaml文件信息 def execute_test_cases(self, api_info): 规范yaml接口信息执行接口、提取结果以及断言操作 :param api_info: :return: # 先打印查看api_info信息 print(api_info) # 添加异常 try: # 获取到接口的服务器地址--通过读取配置文件去获取--通过这个对象去调用 conf_host self.conf.get_host(host) # 获取url--完整的url是通过 服务器地址 加上 接口信息api_info url conf_host api_info[baseInfo][url] # 打印url print(url) except Exception as e: # 因暂时未写日志模块所以这里先打印出来 print(f出现未知异常--{e}) # 调试查看 if __name__ __main__: # 先引进读取方法 read_yaml--传如一个它的相对路径--赋值给新变量data--[0]通过索引去列表值第一个 api_info read_yaml(.././datas/login.yaml)[0] # 实例化类 RequestsBase()--并赋值给一个变量对象 req req RequestsBase() # 通过这个对象req去调用刚刚封装的方法execute_test_cases--把读取到yaml文件的数据api_info传给它 req.execute_test_cases(api_info)10、运行 apiutils.py 文件从结果中拿到了接口的完整地址至此已经成功拿到了 URL可再继续拿到其他关键词信息例如接口名称、请求方式、请求头......等等所需的数据信息获取请求头信息讲解方式一、固定内容11、如若 header 是固定写死的怎么处理header: Content-Type: application/x-www-formurlencoded;charsetUTF-8部分代码示例详情步骤参考如下烦请自行查看# 请求头通过get方法获取看里面有没有请求头如果有的话就返回给变量header若没有则传个默认空值None就行 # 判断请求头是否可选使用get的话当header有的话就返回没有时就返回None不至于报错 header api_info[baseInfo].get(header, None) # 再进行一步处理--判断 header 不为空 if header is not None: # 判断header是什么类型如果是一个字符串str类型的话 就需要给它做一个解析 # if isinstance(header, str) # 调用parse_and_replace_variables方法把header给传进来 # self.parse_and_replace_variables(header) # 若不是字符串直接给他返回header # else header # header 最后再把结果传回给它自己就行了 header self.parse_and_replace_variables(header) if isinstance(header, str) else header # 打印查看--type() 打印类型 print(header, type(header)) # 结果--它是一个dict字典类型 {Content-Type: application/x-www-formurlencoded;charsetUTF-8} class dict它是一个字符串类型因为要把键值对的形式发送到接口请求里面接口请求它只能接收一个字典所以需要转换成字典类型这样去把请求头发送给request模块它才能正确的去解析这个请求因为它只能去接收一个字典类型此时无论header是固定写死的还是通过动态解析变量引用的它都是字典类型如果header是可选的配置文件中就没写如果header不填的话它就返回默认空值它就不执行if后面的语句了。方式二、动态解析12、如若是通过动态解析变量引用的方式 则需要进一步处理需要多判断一下13、 修改 login.yaml 文件内容设置为通过动态解析变量引用方式- baseInfo: api_name: 用户登录 url: /dar/user/login method: post header: ${get_headers(data)} cookies: Cookie: test123456789020241011 testCase: - case_name: 用户正常登录校验 data: user_name: test01 passwd: admin123 validation: - contain: {msg:登录成功} - eq: {msg:登录成功} extract: token: $.token extract_list: goodid: $.good_id14、继续修改 apiutils.py 文件内容增加 if 判断# 导包 import json from unit_tools.handle_data.yaml_handler import read_yaml import re from unit_tools.debugtalk import DebugTalk # 引入 配置文件模块 from unit_tools.handle_data.configParse import ConfigParser # 创建一个类 RequestsBase class RequestsBase: # 在RequestsBase类的里面写一个初始化构造函数 def __init__(self): # 实例化一下实例化之后就可以通过这个对象去获取 host self.conf ConfigParser() # 创建一个方法 parse_and_replace_variables解析并且替换变量--传一个参数 yml_data--要解析的yaml文件的数组 def parse_and_replace_variables(self, yml_data): 解析并替换YAML文件数据中的变量引用如${get_extract_data(goodsId,1)} :param yml_data: 解析的YAML数据 :return: 最终要返回的是dict类型--才能给后续的一些方法调用时使用 # if isinstance(yml_data, str)--判断传进来的数据是什么类型--后续需要字符串才能做操作的所以这里先判断是不是字符串格式 # yml_data若是字符串则执行 if 语句左边的代码 直接返回yml_data # yml_data不是字符串--调用json.dumps(yml_data)模块转成字符串--ensure_asciiFalse 参数是可以处理中文 # if else 语句写在一行是 三元运算--通过这一系列之后最终返回一个 新的变量yaml_data_str --它是一个字符串 yml_data_str yml_data if isinstance(yml_data, str) else json.dumps(yml_data, ensure_asciiFalse) # 打印查看 yaml_data_str print(f解析前{yml_data_str}) # 用for循环去判断 字符串yml_data_str里面这种标识 ${ 格式出现的次数--加一个_下划线表示不需要用到这个变量是一个占位符的意思 for _ in range(yml_data_str.count(${)): # if判断这个标识${ 它包含在字符串yml_data_str里面--并且另一半它 } 也包含在字符串yml_data_str里面 if ${ in yml_data_str and } in yml_data_str: # 通过获取到 ${ 标识的起始位置--通过字符串 yml_data_str 的索引 index--获取它的索引的起始位置 strat_index yml_data_str.index(${) # 获取结束位置 标识 }--再加一个参数 它的开头 strat_index end_index yml_data_str.index(}, strat_index) # 拿到开始、结束位置后--通过字符串切片--起始位置、结束位置1--为什么1--因为python中索引切片是左闭右开的一个规则 # 什么是左闭右开--开始位置是包含在切片里面的但是结束元素不包含在切片中所以得1 variable_data yml_data_str[strat_index:end_index 1] # print(f函数名{variable_data}) # 使用正则表达式提取函数名和参数 # 导入re模块--调用re模块中的match方法--里面跟两个参数第一个参数是正则表达式第二个参数是要提取正则表达式的字符串 match re.match(r\$\{(\w)\((.*?)\)\}, variable_data) # 判断一下有没有匹配到--匹配的对象是不是成功了--成功 if match: # 通过match.groups()去拿到函数名还有参数--定义两个变量去接收,第一个函数名func_name、第二个函数值func_params func_name, func_params match.groups() # 先判断它存在--走if左边的代码--给它做一个切片若参数值不存在则返回一个空列表即可 # 参数值有一个或多个--所以需要做一个处理--参数值func_params通过逗号去做一个切片 # 最后再把值重新赋值给它自己 func_params func_params.split(,) if func_params else [] # print(f函数值{func_name}) # 使用面向对象反射getattr调用函数 # 引如类后直接 实例化类--第二个参数传的是一个函数名 func_name--函数调用 (*func_params)--通过星号把参数值func_params去解包传递给参数func_name extract_data getattr(DebugTalk(), func_name)(*func_params) print(f提取到的结果:{extract_data}) # 使用正则表达式替换原始字符串中的变量引用为调用后的结果 # 使用正则表达式替换函数re.sub--第一个参数就是需要替换的哪个参数值第二个参数是解析后得到的数据extract_data第三个参数是原始解析前的yml_data_str yml_data_str re.sub(re.escape(variable_data), str(extract_data), yml_data_str) # 还原数据将其转换为字典类型 # 添加异常处理--像这种转换的最好是加一个异常处理 try: # 直接调用 son.loads(yml_data_str)--把字符串转换成字典 data json.loads(yml_data_str) # json转码异常 except json.JSONDecodeError: # 返回原始数据yml_data_str--赋值给新变量 data data yml_data_str return data # 写个方法 execute_test_cases--传一个参数 接口的信息--即写yaml文件信息 def execute_test_cases(self, api_info): 规范yaml接口信息执行接口、提取结果以及断言操作 :param api_info: :return: # 先打印查看api_info信息 print(api_info) # 添加异常 try: # 获取到接口的服务器地址--通过读取配置文件去获取--通过这个对象去调用 conf_host self.conf.get_host(host) # 获取url--完整的url是通过 服务器地址 加上 接口信息api_info url conf_host api_info[baseInfo][url] # 接口名称 api_name api_info[baseInfo][api_name] # 请求方式 method api_info[baseInfo][method] # 请求头--通过get方法获取看里面有没有请求头--如果有的话就返回给变量header若没有则传个默认空值None就行 # 判断请求头是否可选使用get的话当header有的话就返回没有时就返回None不至于报错 header api_info[baseInfo].get(header, None) # 再进行一步处理--判断 header 不为空 if header is not None: # if isinstance(header, str) 判断header是什么类型--如果是一个字符串str类型的话 就需要给它做一个解析 # self.parse_and_replace_variables(header) 调用parse_and_replace_variables方法把header给传进来 # else header 若不是字符串直接给他返回header # header 最后再把结果传回给它自己就行了 # eval() 调用一个函数 把解析后的数据 self.parse_and_replace_variables(header) 转换为 字典类型 header eval(self.parse_and_replace_variables(header)) if isinstance(header, str) else header # 打印查看--type() 打印类型 print(header, type(header)) except Exception as e: # 因暂时未写日志模块所以这里先打印出来 print(f出现未知异常--{e}) # 调试查看 if __name__ __main__: # 先引进读取方法 read_yaml--传如一个它的相对路径--赋值给新变量data--[0]通过索引去列表值第一个 api_info read_yaml(.././datas/login.yaml)[0] # 实例化类 RequestsBase()--并赋值给一个变量对象 req req RequestsBase() # 通过这个对象req去调用刚刚封装的方法execute_test_cases--把读取到yaml文件的数据api_info传给它 req.execute_test_cases(api_info)15、到这一步请求头已经处理完了继续处理下一个 cookie 字段信息与 header一样处理# 导包 import json from unit_tools.handle_data.yaml_handler import read_yaml import re from unit_tools.debugtalk import DebugTalk # 引入 配置文件模块 from unit_tools.handle_data.configParse import ConfigParser # 创建一个类 RequestsBase class RequestsBase: # 在RequestsBase类的里面写一个初始化构造函数 def __init__(self): # 实例化一下实例化之后就可以通过这个对象去获取 host self.conf ConfigParser() # 创建一个方法 parse_and_replace_variables解析并且替换变量--传一个参数 yml_data--要解析的yaml文件的数组 def parse_and_replace_variables(self, yml_data): 解析并替换YAML文件数据中的变量引用如${get_extract_data(goodsId,1)} :param yml_data: 解析的YAML数据 :return: 最终要返回的是dict类型--才能给后续的一些方法调用时使用 # if isinstance(yml_data, str)--判断传进来的数据是什么类型--后续需要字符串才能做操作的所以这里先判断是不是字符串格式 # yml_data若是字符串则执行 if 语句左边的代码 直接返回yml_data # yml_data不是字符串--调用json.dumps(yml_data)模块转成字符串--ensure_asciiFalse 参数是可以处理中文 # if else 语句写在一行是 三元运算--通过这一系列之后最终返回一个 新的变量yaml_data_str --它是一个字符串 yml_data_str yml_data if isinstance(yml_data, str) else json.dumps(yml_data, ensure_asciiFalse) # 打印查看 yaml_data_str print(f解析前{yml_data_str}) # 用for循环去判断 字符串yml_data_str里面这种标识 ${ 格式出现的次数--加一个_下划线表示不需要用到这个变量是一个占位符的意思 for _ in range(yml_data_str.count(${)): # if判断这个标识${ 它包含在字符串yml_data_str里面--并且另一半它 } 也包含在字符串yml_data_str里面 if ${ in yml_data_str and } in yml_data_str: # 通过获取到 ${ 标识的起始位置--通过字符串 yml_data_str 的索引 index--获取它的索引的起始位置 strat_index yml_data_str.index(${) # 获取结束位置 标识 }--再加一个参数 它的开头 strat_index end_index yml_data_str.index(}, strat_index) # 拿到开始、结束位置后--通过字符串切片--起始位置、结束位置1--为什么1--因为python中索引切片是左闭右开的一个规则 # 什么是左闭右开--开始位置是包含在切片里面的但是结束元素不包含在切片中所以得1 variable_data yml_data_str[strat_index:end_index 1] # print(f函数名{variable_data}) # 使用正则表达式提取函数名和参数 # 导入re模块--调用re模块中的match方法--里面跟两个参数第一个参数是正则表达式第二个参数是要提取正则表达式的字符串 match re.match(r\$\{(\w)\((.*?)\)\}, variable_data) # 判断一下有没有匹配到--匹配的对象是不是成功了--成功 if match: # 通过match.groups()去拿到函数名还有参数--定义两个变量去接收,第一个函数名func_name、第二个函数值func_params func_name, func_params match.groups() # 先判断它存在--走if左边的代码--给它做一个切片若参数值不存在则返回一个空列表即可 # 参数值有一个或多个--所以需要做一个处理--参数值func_params通过逗号去做一个切片 # 最后再把值重新赋值给它自己 func_params func_params.split(,) if func_params else [] # print(f函数值{func_name}) # 使用面向对象反射getattr调用函数 # 引如类后直接 实例化类--第二个参数传的是一个函数名 func_name--函数调用 (*func_params)--通过星号把参数值func_params去解包传递给参数func_name extract_data getattr(DebugTalk(), func_name)(*func_params) print(f提取到的结果:{extract_data}) # 使用正则表达式替换原始字符串中的变量引用为调用后的结果 # 使用正则表达式替换函数re.sub--第一个参数就是需要替换的哪个参数值第二个参数是解析后得到的数据extract_data第三个参数是原始解析前的yml_data_str yml_data_str re.sub(re.escape(variable_data), str(extract_data), yml_data_str) # 还原数据将其转换为字典类型 # 添加异常处理--像这种转换的最好是加一个异常处理 try: # 直接调用 son.loads(yml_data_str)--把字符串转换成字典 data json.loads(yml_data_str) # json转码异常 except json.JSONDecodeError: # 返回原始数据yml_data_str--赋值给新变量 data data yml_data_str return data # 写个方法 execute_test_cases--传一个参数 接口的信息--即写yaml文件信息 def execute_test_cases(self, api_info): 规范yaml接口信息执行接口、提取结果以及断言操作 :param api_info: :return: # 先打印查看api_info信息 print(api_info) # 添加异常 try: # 获取到接口的服务器地址--通过读取配置文件去获取--通过这个对象去调用 conf_host self.conf.get_host(host) # 获取url--完整的url是通过 服务器地址 加上 接口信息api_info url conf_host api_info[baseInfo][url] # 接口名称 api_name api_info[baseInfo][api_name] # 请求方式 method api_info[baseInfo][method] # 请求头--通过get方法获取看里面有没有请求头--如果有的话就返回给变量header若没有则传个默认空值None就行 # 判断请求头是否可选使用get的话当header有的话就返回没有时就返回None不至于报错 header api_info[baseInfo].get(header, None) # 再进行一步处理--判断 header 不为空 if header is not None: # if isinstance(header, str) 判断header是什么类型--如果是一个字符串str类型的话 就需要给它做一个解析 # self.parse_and_replace_variables(header) 调用parse_and_replace_variables方法把header给传进来 # else header 若不是字符串直接给他返回header # header 最后再把结果传回给它自己就行了 # eval() 调用一个函数 把解析后的数据 self.parse_and_replace_variables(header) 转换为 字典类型 header eval(self.parse_and_replace_variables(header)) if isinstance(header, str) else header # 打印查看请求头--type() 打印类型 print(header, type(header)) # cookies 与 请求头一样 cookies api_info[baseInfo].get(cookies, None) if cookies is not None: cookies eval(self.parse_and_replace_variables(cookies)) if isinstance(cookies, str) else cookies # 打印查看cookie--type() 打印类型 print(cookies, type(cookies)) except Exception as e: # 因暂时未写日志模块所以这里先打印出来 print(f出现未知异常--{e}) # 调试查看 if __name__ __main__: # 先引进读取方法 read_yaml--传如一个它的相对路径--赋值给新变量data--[0]通过索引去列表值第一个 api_info read_yaml(.././datas/login.yaml)[0] # 实例化类 RequestsBase()--并赋值给一个变量对象 req req RequestsBase() # 通过这个对象req去调用刚刚封装的方法execute_test_cases--把读取到yaml文件的数据api_info传给它 req.execute_test_cases(api_info)16、至此baseInfo中的数据已经处理完下篇接着继续处理testCase中的数据未完待续。。。