API 接口自动化测试详细图文教程学习系列19--添加封装其他的方法

测试学习记录,仅供参考!

添加其他方法

获取当前系统时间方法

1、 修改项目根目录 unit_tools 软件包debugtalk.py的 Python 文件内容,添加获取当前时间方法;

复制代码
# 导包
import random  # random -- 随机数
import re  # re -- 正则表达式
from unit_tools.handle_data.yaml_handler import get_extract_yaml
import time

# 定义一个类 DebugTalk
class DebugTalk:
    # 定义一个方法 get_extract_data提取全局配置文件extract.yaml变量--第一个参数 node_name;第二个参数这里默认为空,可以传也可以不传
    def get_extract_data(self, node_name, out_format=None):
        """
        获取全局yaml文件extract.yaml数据,首先判断out_format是否为数字类型,如果不是就获取下一个节点的value
        :param node_name: 全局文件extract.yaml文件中的key--例如:Cookie、access_token_cookie、token
        :param out_format: str类型,0:随机去读取;-1:读取全部数据,返回字符串格式;-2:读取全部,返回列表格式;其他值的就按顺序读取
        :return:
        """
        # 导包引入模块之后--直接调用方法get_extract_yaml()--需要传两个参数,暂时先传一个参数 node_name--返回值赋值给变量data
        data = get_extract_yaml(node_name)
        # 若是数值类型的--判断第二个参数不等于空 并且 --加一个布尔值--正则表达式,判断传入的第二个参数是数值的形式还是一个字母参数的形式
        if out_format is not None and bool(re.compile(r'^[+-]?\d+$').match(str(out_format))):
            # 转换格式成int类型--返回赋值给变量out_format
            out_format = int(out_format)
            # 定义一个字典--或者使用if    else
            data_value = {
                # 若传的这个参数out_format存在--通过self.seq_read(data, out_format)调用
                out_format: self.seq_read(data, out_format),
                # 若第二参数传入的是 0 -- 通过随机函数-- 随机去读取
                0: random.choice(data),
                # 传入的是 -1 -- 去读取全部,返回字符串格式--列表怎么转字符串--通过 ','.join(data) 逗号分隔.join() 组成新的字符串
                -1: ','.join(data),
                # 若传入的是 -2 --读取全部,返回列表格式
                -2: ','.join(data).split(',')
            }
            # 通过data_value取值--取第二个值--返回出去赋值给变量data
            data = data_value[out_format]
        else:
            # 若第二个参数传入的不是数字--调用get_extract_yaml方法--把这两个node_name, out_format参数给它传进来
            data = get_extract_yaml(node_name, out_format)
        # return--把data值返回出去
        return data

    # 定义一个方法 seq_read--参入两个参数,第一个data,第二个随机数randoms
    def seq_read(self, data, randoms):
        """        获取extract.yaml,第二个参数不为 0、-1、-2的情况下        """
        # 直接判断这个参数randoms不是0、-1、-2
        if randoms not in [0, -1, -2]:
            # 直接返回一个 data--通过这个 randoms去减1--为什么要减1呢?--
            return data[randoms - 1]
        else:
            return None

    # 获取当前时间
    @classmethod
    def get_now_time(cls):
        return time.time()


# 测试调试查看
if __name__ == '__main__':
    # 实例化类 DebugTalk()
    debug = DebugTalk()
    # 通过对象取调用方法--第一个参数是goodsId
    # 第二个参数(0:随机读取,1~4:按顺序依次读取,-1:读取全部,-2:读取全部,返回列表格式,-3是第一个)
    # 根据实际情况其他超出范围会报"索引错误:列表索引超出范围 " IndexError: list index out of range
    res = debug.get_extract_data('goodsId','0')
    print(res)

2、修改项目根目录 datas 目录文件路径下adduser.yaml文件内容,通过"${}"这种格式去调用对应的函数方法,可以动态的去实现某一个功能,这就是要做热加载的一个原因;

复制代码
- baseInfo:
    api_name: 用户新增
    url: /dar/user/addUser
    method: post
    header:
        Content-Type: application/x-www-formurlencoded;charset=UTF-8
  testCase:
     - case_name: 用户新增正常校验
       data:
         username: testadduser
         password: tset6789890
         role_id: 123456789
         dates: ${get_now_time()}
         phone: 13800000000
         token: ${get_extract_data(goodsId,1)}

3、在项目根目录 unit_tools 软件包下新建名称为apiutils.py的 Python 文件,输入以下内容;

复制代码
# 导包
import json
from unit_tools.handle_data.yaml_handler import read_yaml

# 创建一个类 RequestsBase
class RequestsBase:
    # 创建一个方法 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:
        """
        # if isinstance(yml_data, str)--判断传进来的数据是什么类型?--后续需要字符串才能做操作的,所以这里先判断是不是字符串格式
        # yml_data若是字符串则执行 if 语句左边的代码 直接返回yml_data
        # yml_data不是字符串--调用json.dumps(yml_data)模块转成字符串--ensure_ascii=False 参数是可以处理中文
        # if else 语句写在一行是 三元运算--通过这一系列之后最终返回一个 新的变量yaml_data_str --它是一个字符串
        yaml_data_str = yml_data if isinstance(yml_data, str) else json.dumps(yml_data, ensure_ascii=False)
        # 打印查看 yaml_data_str
        print(f'解析前的类型:{type(yaml_data_str)},解析前:{yaml_data_str}')

# 测试调试查看
if __name__ == '__main__':
    # 先引进读取方法 read_yaml--传如一个它的相对路径--赋值给新变量data--[0]通过索引去列表值第一个
    data = read_yaml('.././datas/adduser.yaml')[0]
    # 打印
    print(data)
    # 实例化类 RequestsBase()--并赋值给一个变量对象 req
    req = RequestsBase()
    # 通过类对象去调用parse_and_replace_variables(data) 这个解析的方法--把读取到的 data 这个yaml数据传给它
    req.parse_and_replace_variables(data)

4、运行查看,从打印结果中可以看出,解析前是一个字符串格式类型,而所需要的数据("{get_now_time}"、"${get_extract_data(goodsId,1)}")解析后类型是一个字典格式;

复制代码
# 打印结果--可以看到 解析前是一个字符串,解析后是一个字典格式
{'baseInfo': {'api_name': '用户新增', 'url': '/dar/user/addUser', 'method': 'post', 'header': {'Content-Type': 'application/x-www-formurlencoded;charset=UTF-8'}}, 'testCase': [{'case_name': '用户新增正常校验', 'data': {'username': 'testadduser', 'password': 'tset6789890', 'role_id': 123456789, 'dates': '${get_now_time()}', 'phone': 13800000000, 'token': '${get_extract_data(goodsId,1)}'}}]}
解析前的类型:<class 'str'>,解析前:{"baseInfo": {"api_name": "用户新增", "url": "/dar/user/addUser", "method": "post", "header": {"Content-Type": "application/x-www-formurlencoded;charset=UTF-8"}}, "testCase": [{"case_name": "用户新增正常校验", "data": {"username": "testadduser", "password": "tset6789890", "role_id": 123456789, "dates": "${get_now_time()}", "phone": 13800000000, "token": "${get_extract_data(goodsId,1)}"}}]}

5、修改apiutils.py文件,使用 for 循环去判断字符串 yaml_data_str 里面的标识符,从而获取到所需要的数据(详情烦请自行查看每行代码注释);

复制代码
# 导包
import json
from unit_tools.handle_data.yaml_handler import read_yaml


# 创建一个类 RequestsBase
class RequestsBase:
    # 创建一个方法 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:
        """
        # if isinstance(yml_data, str)--判断传进来的数据是什么类型?--后续需要字符串才能做操作的,所以这里先判断是不是字符串格式
        # yml_data若是字符串则执行 if 语句左边的代码 直接返回yml_data
        # yml_data不是字符串--调用json.dumps(yml_data)模块转成字符串--ensure_ascii=False 参数是可以处理中文
        # if else 语句写在一行是 三元运算--通过这一系列之后最终返回一个 新的变量yaml_data_str --它是一个字符串
        yaml_data_str = yml_data if isinstance(yml_data, str) else json.dumps(yml_data, ensure_ascii=False)
        # 打印查看 yaml_data_str
        print(f'解析前的类型:{type(yaml_data_str)},解析前:{yaml_data_str}')
        # 用for循环去判断 字符串yaml_data_str里面这种标识 "${" 格式出现的次数--加一个'_'下划线表示不需要用到这个变量,是一个占位符的意思
        for _ in range(yaml_data_str.count("${")):
            # if判断这个标识'${' 它包含在字符串yaml_data_str里面--并且另一半它 '}' 也包含在字符串yaml_data_str里面
            if '${' in yaml_data_str and '}' in yaml_data_str:
                # 通过获取到 "${" 标识的起始位置--通过字符串 yaml_data_str 的索引 index--获取它的索引的起始位置
                strat_index = yaml_data_str.index("${")
                # 获取结束位置 标识 "}"--再加一个参数 它的开头 strat_index
                end_index = yaml_data_str.index("}", strat_index)
                # 拿到开始、结束位置后--通过字符串切片--起始位置、结束位置+1--为什么+1?--因为python中索引切片是左闭右开的一个规则
                # 什么是左闭右开?--开始位置是包含在切片里面的,但是结束元素不包含在切片中,所以得+1
                variable_data = yaml_data_str[strat_index:end_index + 1]
                # 打印查看 variable_data
                print(variable_data)


# 调试查看
if __name__ == '__main__':
    # 先引进读取方法 read_yaml--传如一个它的相对路径--赋值给新变量data--[0]通过索引去列表值第一个
    data = read_yaml('.././datas/adduser.yaml')[0]
    # 打印
    print(data)
    # 实例化类 RequestsBase()--并赋值给一个变量对象 req
    req = RequestsBase()
    # 通过类对象去调用parse_and_replace_variables(data) 这个解析的方法--把读取到的 data 这个yaml数据传给它
    req.parse_and_replace_variables(data)

6、再次执行查看,可以看到会打印两个一模一样的数据,跟配置文件中的两个数据("{get_now_time}"、"{get_extract_data(goodsId,1)}")是不一样的?这是因为代码在循环中每次都是从原始的字符串中去查找这个标识(起始、结束位置),所以会重复查找相同的变量;

复制代码
{'baseInfo': {'api_name': '用户新增', 'url': '/dar/user/addUser', 'method': 'post', 'header': {'Content-Type': 'application/x-www-formurlencoded;charset=UTF-8'}}, 'testCase': [{'case_name': '用户新增正常校验', 'data': {'username': 'testadduser', 'password': 'tset6789890', 'role_id': 123456789, 'dates': '${get_now_time()}', 'phone': 13800000000, 'token': '${get_extract_data(goodsId,1)}'}}]}
解析前的类型:<class 'str'>,解析前:{"baseInfo": {"api_name": "用户新增", "url": "/dar/user/addUser", "method": "post", "header": {"Content-Type": "application/x-www-formurlencoded;charset=UTF-8"}}, "testCase": [{"case_name": "用户新增正常校验", "data": {"username": "testadduser", "password": "tset6789890", "role_id": 123456789, "dates": "${get_now_time()}", "phone": 13800000000, "token": "${get_extract_data(goodsId,1)}"}}]}
${get_now_time()}
${get_now_time()}
使用正则表达式提取函数名和参数

正则表达式烦请自行学习了解,可参考教程:Python3 正则表达式

|--------|---|---|
| 正则表达式: |||
| \ | 第一步,先拿到 这一部分,怎么写呢?--斜杠 美元符号 \$ ||
| \{ | 花括号或者大括号的左边位置 { -- 再来一个 斜杠 花括号 \{ ||
| (\w+) | 处理函数名,这里使用一个组给它括起来 () -- \w 匹配 字母、数字、下划线 组成 -- 再加一个 加号 + 可以匹配一个或者多个 字母、数字、下划线-- (\w+) ||
| \( | 函数名后面的括号 () --先左边位置 斜杠 小括号 \( ||
| (.*?) | 参数名,有的有参数,有的没有,同样需要处理,怎么处理呢?--需要用一个贪婪匹配,就是 .*? --点 星号 问号--就是第二个捕获组,匹配任意字符--尽量去匹配一个最短的长度,用于匹配参数--(.*?) ||
| \) | 小括号 右边的括号 \) ||
| \} | 最外层右边的花括号 \} ||

7、修改apiutils.py文件内容,使用正则表达式来提取所需要的函数名和参数;

复制代码
# 导包
import json
from unit_tools.handle_data.yaml_handler import read_yaml
import re

# 创建一个类 RequestsBase
class RequestsBase:
    # 创建一个方法 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:
        """
        # if isinstance(yml_data, str)--判断传进来的数据是什么类型?--后续需要字符串才能做操作的,所以这里先判断是不是字符串格式
        # yml_data若是字符串则执行 if 语句左边的代码 直接返回yml_data
        # yml_data不是字符串--调用json.dumps(yml_data)模块转成字符串--ensure_ascii=False 参数是可以处理中文
        # if else 语句写在一行是 三元运算--通过这一系列之后最终返回一个 新的变量yaml_data_str --它是一个字符串
        yaml_data_str = yml_data if isinstance(yml_data, str) else json.dumps(yml_data, ensure_ascii=False)
        # 打印查看 yaml_data_str
        print(f'解析前的类型:{type(yaml_data_str)},解析前:{yaml_data_str}')
        # 用for循环去判断 字符串yaml_data_str里面这种标识 "${" 格式出现的次数--加一个'_'下划线表示不需要用到这个变量,是一个占位符的意思
        for _ in range(yaml_data_str.count("${")):
            # if判断这个标识'${' 它包含在字符串yaml_data_str里面--并且另一半它 '}' 也包含在字符串yaml_data_str里面
            if '${' in yaml_data_str and '}' in yaml_data_str:
                # 通过获取到 "${" 标识的起始位置--通过字符串 yaml_data_str 的索引 index--获取它的索引的起始位置
                strat_index = yaml_data_str.index("${")
                # 获取结束位置 标识 "}"--再加一个参数 它的开头 strat_index
                end_index = yaml_data_str.index("}", strat_index)
                # 拿到开始、结束位置后--通过字符串切片--起始位置、结束位置+1--为什么+1?--因为python中索引切片是左闭右开的一个规则
                # 什么是左闭右开?--开始位置是包含在切片里面的,但是结束元素不包含在切片中,所以得+1
                variable_data = yaml_data_str[strat_index:end_index + 1]
                # 打印查看 variable_data
                # print(variable_data)

                # 使用正则表达式提取函数名和参数
                # 导入re模块--调用re模块中的match方法--里面跟两个参数,第一个参数是正则表达式,第二个参数是要提取正则表达式的字符串
                match = re.match(r'\$\{(\w+)\((.*?)\)\}', variable_data)
                # 打印
                print(match)


# 调试查看
if __name__ == '__main__':
    # 先引进读取方法 read_yaml--传如一个它的相对路径--赋值给新变量data--[0]通过索引去列表值第一个
    data = read_yaml('.././datas/adduser.yaml')[0]
    # 打印
    print(data)
    # 实例化类 RequestsBase()--并赋值给一个变量对象 req
    req = RequestsBase()
    # 通过类对象去调用parse_and_replace_variables(data) 这个解析的方法--把读取到的 data 这个yaml数据传给它
    req.parse_and_replace_variables(data)

8、运行成功后发现它们俩个还是一样的,继续下一步;

9、修改apiutils.py文件内容,添加判断;

复制代码
# 导包
import json
from unit_tools.handle_data.yaml_handler import read_yaml
import re

# 创建一个类 RequestsBase
class RequestsBase:
    # 创建一个方法 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:
        """
        # if isinstance(yml_data, str)--判断传进来的数据是什么类型?--后续需要字符串才能做操作的,所以这里先判断是不是字符串格式
        # yml_data若是字符串则执行 if 语句左边的代码 直接返回yml_data
        # yml_data不是字符串--调用json.dumps(yml_data)模块转成字符串--ensure_ascii=False 参数是可以处理中文
        # if else 语句写在一行是 三元运算--通过这一系列之后最终返回一个 新的变量yaml_data_str --它是一个字符串
        yaml_data_str = yml_data if isinstance(yml_data, str) else json.dumps(yml_data, ensure_ascii=False)
        # 打印查看 yaml_data_str
        print(f'解析前的类型:{type(yaml_data_str)},解析前:{yaml_data_str}')
        # 用for循环去判断 字符串yaml_data_str里面这种标识 "${" 格式出现的次数--加一个'_'下划线表示不需要用到这个变量,是一个占位符的意思
        for _ in range(yaml_data_str.count("${")):
            # if判断这个标识'${' 它包含在字符串yaml_data_str里面--并且另一半它 '}' 也包含在字符串yaml_data_str里面
            if '${' in yaml_data_str and '}' in yaml_data_str:
                # 通过获取到 "${" 标识的起始位置--通过字符串 yaml_data_str 的索引 index--获取它的索引的起始位置
                strat_index = yaml_data_str.index("${")
                # 获取结束位置 标识 "}"--再加一个参数 它的开头 strat_index
                end_index = yaml_data_str.index("}", strat_index)
                # 拿到开始、结束位置后--通过字符串切片--起始位置、结束位置+1--为什么+1?--因为python中索引切片是左闭右开的一个规则
                # 什么是左闭右开?--开始位置是包含在切片里面的,但是结束元素不包含在切片中,所以得+1
                variable_data = yaml_data_str[strat_index:end_index + 1]
                # 打印查看 variable_data
                # print(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()
                    # 打印函数名--结果又是两个一样的
                    print(func_name)


# 调试查看
if __name__ == '__main__':
    # 先引进读取方法 read_yaml--传如一个它的相对路径--赋值给新变量data--[0]通过索引去列表值第一个
    data = read_yaml('.././datas/adduser.yaml')[0]
    # 打印
    print(data)
    # 实例化类 RequestsBase()--并赋值给一个变量对象 req
    req = RequestsBase()
    # 通过类对象去调用parse_and_replace_variables(data) 这个解析的方法--把读取到的 data 这个yaml数据传给它
    req.parse_and_replace_variables(data)

10、运行查看结果仍是一样的,接着下一步;

11、修改apiutils.py文件内容;

复制代码
# 导包
import json
from unit_tools.handle_data.yaml_handler import read_yaml
import re

# 创建一个类 RequestsBase
class RequestsBase:
    # 创建一个方法 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:
        """
        # if isinstance(yml_data, str)--判断传进来的数据是什么类型?--后续需要字符串才能做操作的,所以这里先判断是不是字符串格式
        # yml_data若是字符串则执行 if 语句左边的代码 直接返回yml_data
        # yml_data不是字符串--调用json.dumps(yml_data)模块转成字符串--ensure_ascii=False 参数是可以处理中文
        # if else 语句写在一行是 三元运算--通过这一系列之后最终返回一个 新的变量yaml_data_str --它是一个字符串
        yaml_data_str = yml_data if isinstance(yml_data, str) else json.dumps(yml_data, ensure_ascii=False)
        # 打印查看 yaml_data_str
        print(f'解析前的类型:{type(yaml_data_str)},解析前:{yaml_data_str}')
        # 用for循环去判断 字符串yaml_data_str里面这种标识 "${" 格式出现的次数--加一个'_'下划线表示不需要用到这个变量,是一个占位符的意思
        for _ in range(yaml_data_str.count("${")):
            # if判断这个标识'${' 它包含在字符串yaml_data_str里面--并且另一半它 '}' 也包含在字符串yaml_data_str里面
            if '${' in yaml_data_str and '}' in yaml_data_str:
                # 通过获取到 "${" 标识的起始位置--通过字符串 yaml_data_str 的索引 index--获取它的索引的起始位置
                strat_index = yaml_data_str.index("${")
                # 获取结束位置 标识 "}"--再加一个参数 它的开头 strat_index
                end_index = yaml_data_str.index("}", strat_index)
                # 拿到开始、结束位置后--通过字符串切片--起始位置、结束位置+1--为什么+1?--因为python中索引切片是左闭右开的一个规则
                # 什么是左闭右开?--开始位置是包含在切片里面的,但是结束元素不包含在切片中,所以得+1
                variable_data = yaml_data_str[strat_index:end_index + 1]
                # 打印查看 variable_data
                # print(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(func_params)


# 调试查看
if __name__ == '__main__':
    # 先引进读取方法 read_yaml--传如一个它的相对路径--赋值给新变量data--[0]通过索引去列表值第一个
    data = read_yaml('.././datas/adduser.yaml')[0]
    # 打印
    print(data)
    # 实例化类 RequestsBase()--并赋值给一个变量对象 req
    req = RequestsBase()
    # 通过类对象去调用parse_and_replace_variables(data) 这个解析的方法--把读取到的 data 这个yaml数据传给它
    req.parse_and_replace_variables(data)

12、运行查看打印结果是空列表,这是因为始终读取的都是第一个 "${get_now_time()}" ,它里面没有参数值;

13、修改apiutils.py文件内容;

复制代码
# 导包
import json
from unit_tools.handle_data.yaml_handler import read_yaml
import re
from unit_tools.debugtalk import DebugTalk


# 创建一个类 RequestsBase
class RequestsBase:
    # 创建一个方法 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)

                    # 使用正则表达式替换原始字符串中的变量引用为调用后的结果
                    # 使用正则表达式替换函数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


# 调试查看
if __name__ == '__main__':
    # 先引进读取方法 read_yaml--传如一个它的相对路径--赋值给新变量data--[0]通过索引去列表值第一个
    data = read_yaml('.././datas/adduser.yaml')[0]
    # 实例化类 RequestsBase()--并赋值给一个变量对象 req
    req = RequestsBase()
    # 通过类对象去调用parse_and_replace_variables(data) 这个解析的方法--把读取到的 data 这个yaml数据传给它
    res = req.parse_and_replace_variables(data)
    print(f'解析后:{res}')

14、运行查看打印结果,可以看到"解析前、解析后"的数据信息,函数名、函数值它们两个不在是同样的了;

复制代码
解析前:{"baseInfo": {"api_name": "用户新增", "url": "/dar/user/addUser", "method": "post", "header": {"Content-Type": "application/x-www-formurlencoded;charset=UTF-8"}}, "testCase": [{"case_name": "用户新增正常校验", "data": {"username": "testadduser", "password": "tset6789890", "role_id": 123456789, "dates": "${get_now_time()}", "phone": 13800000000, "token": "${get_extract_data(goodsId,1)}"}}]}
函数名:${get_now_time()}
函数值:get_now_time
函数名:${get_extract_data(goodsId,1)}
函数值:get_extract_data
解析后:{'baseInfo': {'api_name': '用户新增', 'url': '/dar/user/addUser', 'method': 'post', 'header': {'Content-Type': 'application/x-www-formurlencoded;charset=UTF-8'}}, 'testCase': [{'case_name': '用户新增正常校验', 'data': {'username': 'testadduser', 'password': 'tset6789890', 'role_id': 123456789, 'dates': '1684646092.8515406', 'phone': 13800000000, 'token': '11111111111'}}]}
总结详细操作步骤
复制代码
首先,定义了一个方法   def parse_and_replace_variables(self, yml_data): 
    
然后,传入了一个变量 yml_data --这个变量就是需要解析的一个yaml文件的数据;
第一行代码 yml_data_str = yml_data if isinstance(yml_data, str) else json.dumps(yml_data, ensure_ascii=False) 
    先去判断一下 if isinstance(yml_data, str) 传进来的数据 它是不是一个字符串
    如果是字符串,直接返回回去 yml_data
    若不是字符串,调用json序列化 json.dumps(yml_data, ensure_ascii=False) 序列化之后把它转成字符串
    最终存到一个新的变量 yml_data_str 里面去

再然后通过下一步使用for循环 for _ in range(yml_data_str.count("${")): 
    去循环字符串有没有自定义的标识出现的一个次数--这个次数取决于字符串中它出现多少次;这个循环还用于处理所有变量的引用;
    通过if判断 if '${' in yml_data_str and '}' in yml_data_str: 这个标识是否包含在字符串里面
        如果包含的话,它就找到起始位置,找到起始位置之后再找到结束位置--通过开始、结束位置就可以获取到需要引用的字符串,通过起止位置可以对字符串进行切片,通过切片可以拿到引用的数据,切片引用需要注意"左闭右开"的规则;
        使用正则表达式去提取 需要的这个字符串--这个字符串里面的函数名,还有它的参数,有参数提取它的参数
        通过这种方式去拿到要匹配的对象
        当匹配成功的话
            从匹配的对象中去获取函数名还有它的参数--对参数值做一个切片,分割为一个列表
            然后使用  *func_params  给它传递就行了
            到这一步就是  extract_data = getattr(DebugTalk(), func_name)(*func_params)  使用python里面类的一个反射把结果给返回出去
            最后需要做一个替换,就是把原始的字符串里面这种格式替换成 调用后一个结果 extract_data ,再把最后得到的结果给还原一下,转换成一个字典类型--整个过程就是这样的。

优化请求头传输格式

15、优化修改debugtalk.py文件内容,添加请求头处理方法;

复制代码
# 导包
import random  # random -- 随机数
import re  # re -- 正则表达式
from unit_tools.handle_data.yaml_handler import get_extract_yaml
import time


# 定义一个类 DebugTalk
class DebugTalk:
    # 定义一个方法 get_extract_data提取全局配置文件extract.yaml变量--第一个参数 node_name;第二个参数这里默认为空,可以传也可以不传
    def get_extract_data(self, node_name, out_format=None):
        """
        获取全局yaml文件extract.yaml数据,首先判断out_format是否为数字类型,如果不是就获取下一个节点的value
        :param node_name: 全局文件extract.yaml文件中的key--例如:Cookie、access_token_cookie、token
        :param out_format: str类型,0:随机去读取;-1:读取全部数据,返回字符串格式;-2:读取全部,返回列表格式;其他值的就按顺序读取
        :return:
        """
        # 导包引入模块之后--直接调用方法get_extract_yaml()--需要传两个参数,暂时先传一个参数 node_name--返回值赋值给变量data
        data = get_extract_yaml(node_name)
        # 若是数值类型的--判断第二个参数不等于空 并且 --加一个布尔值--正则表达式,判断传入的第二个参数是数值的形式还是一个字母参数的形式
        if out_format is not None and bool(re.compile(r'^[+-]?\d+$').match(str(out_format))):
            # 转换格式成int类型--返回赋值给变量out_format
            out_format = int(out_format)
            # 定义一个字典--或者使用if    else
            data_value = {
                # 若传的这个参数out_format存在--通过self.seq_read(data, out_format)调用
                out_format: self.seq_read(data, out_format),
                # 若第二参数传入的是 0 -- 通过随机函数-- 随机去读取
                0: random.choice(data),
                # 传入的是 -1 -- 去读取全部,返回字符串格式--列表怎么转字符串--通过 ','.join(data) 逗号分隔.join() 组成新的字符串
                -1: ','.join(data),
                # 若传入的是 -2 --读取全部,返回列表格式
                -2: ','.join(data).split(',')
            }
            # 通过data_value取值--取第二个值--返回出去赋值给变量data
            data = data_value[out_format]
        else:
            # 若第二个参数传入的不是数字--调用get_extract_yaml方法--把这两个node_name, out_format参数给它传进来
            data = get_extract_yaml(node_name, out_format)
        # return--把data值返回出去
        return data

    # 定义一个方法 seq_read--参入两个参数,第一个data,第二个随机数randoms
    def seq_read(self, data, randoms):
        """        获取extract.yaml,第二个参数不为 0、-1、-2的情况下        """
        # 直接判断这个参数randoms不是0、-1、-2
        if randoms not in [0, -1, -2]:
            # 直接返回一个 data--通过这个 randoms去减1--为什么要减1呢?--
            return data[randoms - 1]
        else:
            return None

    # 获取当前时间
    @classmethod
    def get_now_time(cls):
        return time.time()

    # 定义方法 get_headers--获取请求头--传入一个参数类型params_type
    def get_headers(self, params_type):
        """
        获取请求头
        :param params_type: 参数类型:例如'data'或者'json'
        :return:
        """
        # 先约定请求头--定义一个字典 headers_mapping
        headers_mapping = {
            # 如果是data类型--请求头  需要一个字典类型
            'data': {'Content-Type': 'application/x-www-formurlencoded;charset=UTF-8'},
            # 若是json类型--请求头  这里参数名一样的,参数值不一样
            'json': {'Content-Type': 'application/json;charset=UTF-8'}
        }
        # 通过这个字典取值去获取 headers_mapping.get(params_type)--传入参数 params_type
        header = headers_mapping.get(params_type)
        # 判断 header 是否为空
        if header is None:
            # 若是空的话,手动抛出异常
            raise ValueError('不支持其他类型的请求头设置!')
        # 最后要把结果返回出去
        return header


# 测试调试查看
if __name__ == '__main__':
    # 实例化类 DebugTalk()
    debug = DebugTalk()
    # 通过对象取调用方法--第一个参数是goodsId
    # 第二个参数(0:随机读取,1~4:按顺序依次读取,-1:读取全部,-2:读取全部,返回列表格式,-3是第一个)
    # 根据实际情况其他超出范围会报"索引错误:列表索引超出范围 " IndexError: list index out of range
    res = debug.get_extract_data('goodsId', '0')
    print(res)

16、修改项目根目录 datas 目录文件路径下adduser.yaml文件请求头相关内容,把刚刚定义的请求头get_headers方法添加到对应请求头header的位置下(${get_headers(data)});

复制代码
- 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: ${get_now_time()}
         phone: 13800000000
         token: ${get_extract_data(goodsId,1)}

17、优化apiutils.py文件内容,验证新定义封装的请求头处理方法;

复制代码
# 导包
import json
from unit_tools.handle_data.yaml_handler import read_yaml
import re
from unit_tools.debugtalk import DebugTalk


# 创建一个类 RequestsBase
class RequestsBase:
    # 创建一个方法 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


# 调试查看
if __name__ == '__main__':
    # 先引进读取方法 read_yaml--传如一个它的相对路径--赋值给新变量data--[0]通过索引去列表值第一个
    data = read_yaml('.././datas/adduser.yaml')[0]
    # 实例化类 RequestsBase()--并赋值给一个变量对象 req
    req = RequestsBase()
    # 通过类对象去调用parse_and_replace_variables(data) 这个解析的方法--把读取到的 data 这个yaml数据传给它
    res = req.parse_and_replace_variables(data)
    print(f'解析后:{res}')

解析前:{"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": "${get_now_time()}", "phone": 13800000000, "token": "${get_extract_data(goodsId,1)}"}}]}
提取到的结果:{'Content-Type': 'application/x-www-formurlencoded;charset=UTF-8'}
提取到的结果:1684646092.8515406
提取到的结果:11111111111
解析后:{'baseInfo': {'api_name': '用户新增', 'url': '/dar/user/addUser', 'method': 'post', 'header': "{'Content-Type': 'application/x-www-formurlencoded;charset=UTF-8'}"}, 'testCase': [{'case_name': '用户新增正常校验', 'data': {'username': 'testadduser', 'password': 'tset6789890', 'role_id': 123456789, 'dates': '1684646092.8515406', 'phone': 13800000000, 'token': '11111111111'}}]}

未完待续。。。

相关推荐
落痕的寒假3 小时前
[深度学习] 大模型学习8上-推理部署框架llama.cpp与Ollama使用指北
深度学习·学习·llama
Co_Hui3 小时前
Java: 集合
java·开发语言
ch.ju3 小时前
Java程序设计(第3版)第四章——动态部分
java·开发语言
诙_3 小时前
C++学习总结
开发语言·c++·学习
xieliyu.3 小时前
我的第 128 天创作里程碑:从 C 语言入门到 Java 学习之路
学习
2401_865439633 小时前
探索JavaScript对象创建的灵活方式
开发语言·javascript·ecmascript
程序猿~厾罗3 小时前
回归更新,一个简单的重新认识
开发语言
我命由我123453 小时前
Android 开发:Unable to start service Intent { ... } U=0: not found
android·开发语言·android studio·android jetpack·android-studio·android runtime
Kingairy3 小时前
主流AI 七层关系:Token→提示词→上下文→Agent→Harness→MCP→Skills
人工智能·测试工具