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_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中的数据;

未完待续。。。

相关推荐
-To be number.wan1 小时前
计算机组成原理 | SRAM与DRAM
学习·计算机组成原理
zhangfeng11331 小时前
ai 模型加密,强化版终极防盗方案 支持烧录的显卡列表
人工智能·pytorch·python
半个落月1 小时前
深入理解 Python dict 与 set:从哈希表底层到高性能实战
python
带派擂总1 小时前
Python全栈开发 Day10_用户管理系统
python
databook1 小时前
用 SymPy 解决 Manim 曲线绘制速度不均的问题
python·数学·动效
宇宙无敌程序员菜鸟2 小时前
浅玩CRUD Agent
python
程序大视界2 小时前
【Python系列课程】Python入门教程
开发语言·人工智能·python
morning_judger2 小时前
Agent系列(二)-记忆系统的设计
开发语言·python·机器学习
RSTJ_16252 小时前
PYTHON+AI LLM DAY SIXTY-ONE
开发语言·python