测试学习记录,仅供参考!
如何用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_name=None, url=url, method=method, header=header, case_name=None,
cookie=None, file=None, data=req_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(scope='session', autouse=True)
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_name=None, url=url, method=method, header=header, case_name=None, cookie=None, file=None, data=req_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;charset=UTF-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格式提交就填写json;
- files(可选):文件上传接口;
- validation : 断言结果,有多种断言模式,如:包含断言 contain、相等断言 eq、不相等断言 nq、数据库断言 db;
- extract (可选) : 提取单个值,接口的返回值提取,存放在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_ascii=False 参数是可以处理中文
# if else 语句写在一行是 三元运算--通过这一系列之后最终返回一个 新的变量yaml_data_str --它是一个字符串
yml_data_str = yml_data if isinstance(yml_data, str) else json.dumps(yml_data, ensure_ascii=False)
# 打印查看 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;charset=UTF-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;charset=UTF-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_id
14、继续修改 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_ascii=False 参数是可以处理中文
# if else 语句写在一行是 三元运算--通过这一系列之后最终返回一个 新的变量yaml_data_str --它是一个字符串
yml_data_str = yml_data if isinstance(yml_data, str) else json.dumps(yml_data, ensure_ascii=False)
# 打印查看 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_ascii=False 参数是可以处理中文
# if else 语句写在一行是 三元运算--通过这一系列之后最终返回一个 新的变量yaml_data_str --它是一个字符串
yml_data_str = yml_data if isinstance(yml_data, str) else json.dumps(yml_data, ensure_ascii=False)
# 打印查看 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中的数据;
未完待续。。。