Pytest+requests进行接口自动化测试2.0(yaml)

接口自动化测试学习

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
  - data2

test_case:         # 嵌套字典写法
  case1: 有效登录
  case2: 无效登录

test: {name: liu,age: 18}  # 内联写法

long_str: >        # 多行字符串写法
  这是一段
  换行的文字

3. 案例练习与讲解(以登录接口为例)

- baseInfo:
    api_name: 用户登录
    url: /dar/user/login    --- 这里是相对路径,通常会拼接 base_url
    method: Post
    header:
      Content-Type: application/x-www-form-urlencoded;charset=UTF-8
  test_case:
    - case_name: 用户名和密码正确登录验证
      data:                     ---  根据请求头设置data,json
        user_name: test01
        password: admin123
      validation:
        - eq: [msg, 登录成功]
      extract:           --- 从响应 JSON 中提取 token 字段,保存为变量 token
        token: $.token   --- JSONPath 语法,表示根节点下的 token 字段
        
    - case_name: 用户名和密码错误登录验证
      data:                  
        user_name: test0123
        password: admin123
      validation:
        - 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 字段,保存为变量 token
  token: $.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 yaml

def 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_date
    except Exception as e:
        raise e

if __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 eraise 是 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 yaml

class ReadYamlData:
    """读取 YAML 数据"""
    def __init__(self, yaml_file='login.yaml'):
        self.yaml_file = 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}")
  • 解释分析
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() 拼接系统路径保证跨平台兼容(不能用于拼接URL)
使用 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, …)安全拼接当前系统路径(不能用于拼接URL)
os.path.basename(path)获取文件名(如 ‘file.txt’)
os.path.dirname(path)获取目录部分,例如:dirname(‘output/data.yaml’) → ‘output’
os.path.splitext(path)分离文件名和扩展名

(注:URL拼接可使用urljoin)

from urllib.parse import urljoin
        url = urljoin(f"{base_url}",case_info['baseInfo']['url'])

2. 封装一个WriteYamlData 类:写入另一个YAML 文件

  • 整体展示
class WriteYamlData:
    """写入 YAML 数据"""
    def __init__(self, yaml_file='extract.yaml'):
        self.yaml_file = yaml_file

    def 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' + url
    data = 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.post
    token = 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/login
    method: Post
    header:
      Content-Type: application/x-www-form-urlencoded;charset=UTF-8
  test_case:
    - case_name: 用户名和密码正确登录验证
      data:                     # 根据请求头设置data,json
        user_name: test01
        password: admin123
      validation:
        - 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_data
    except 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   ---- 拼接成完整URL
    method = 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: 登录获取 token
  extract:
    token: $.data.token        # 使用 JSONPath 提取
    user_id: $.data.user.id
  • 后续测试用例中引用
- case_name: 获取用户信息
  request:
    method: GET
    url: /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}}"        --- 明文自动 MD5
  data: "{{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: /login
    method: POST
    json:
      username: "{user_info[0]}"
      password: "{user_info[1]}"

7)条件判断与跳过(高级)

- name: 只有登录成功才执行
  request:
    url: /delete/user
  skip_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.tokenJSONPath 提取

2. 案例:动态参数 token 的替换

{{get_data(TOKEN)}} 类型的动态参数均可使用(支持同一yaml文档多个)

1)创建 yaml 接口文档 login.yaml,将 token 设为动态参数

    header:
      Content-Type: application/json
      TOKEN: "{{get_data(TOKEN)}}"

2)将读取的接口返回数据写入指定的 extract.yaml 文件(参考 : 三、yaml文件的写入)

class WriteYamlData:
    """写入 YAML 数据"""
    def __init__(self, yaml_file='extract.yaml'):
        self.yaml_file = yaml_file

    def 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_file
    def 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 None


        with 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_datas

if __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 json


class BaseRequest:
    def __init__(self):
        self.read = ReadYamlData()

    def replace_load(self,data):
        """yaml文件替换解析有{{ }}格式的数据"""

        str_data = data
        if not isinstance(data,str):
            str_data = json.dumps(data,ensure_ascii=False)
        print('yaml文件替换解析前',str_data)

        start = 0
        while True:
            s = str_data.find('{{', start)   # 从str_data第 0 个位置开始找
            if s == -1: break
            b = str_data.find('(', s)       # 从str_data第 s 个位置开始找 (
            if b == -1: break
            e = str_data.find(')', b)       # 从str_data第 b 个位置开始找 )
            if e == -1: break
            func_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)
            continue

        if data and isinstance(data,dict):
            data = json.loads(str_data)
        else:
            data = str_data
        return data

if __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 = data
        if not isinstance(data,str):
            str_data = json.dumps(data,ensure_ascii=False)   
        print('yaml文件替换解析前',str_data)

循环查找 {{ func(…) }} 取出函数名 get_data,参数 TOKEN ,和需替换的内容

 start = 0
        while True:
            s = str_data.find('{{', start)   # 从str_data第 0 个位置开始找
            if s == -1: break
            b = str_data.find('(', s)       # 从str_data第 s 个位置开始找 (
            if b == -1: break
            e = str_data.find(')', b)       # 从str_data第 b 个位置开始找 )
            if e == -1: break
            func_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_data
        return data

在这里插入图片描述

### 使用 Pythonpytestrequests 和 Allure 进行接口自动化测试 #### 准备工作 为了构建一个完整的接口自动化测试框架,需要安装几个必要的包。可以通过 pip 安装这些依赖项: ```bash pip install requests pytest allure-pytest pyyaml openpyxl ``` 这将确保环境中有足够的工具来编写和运行测试。 #### 测试结构设计 创建合理的文件夹结构有助于更好地管理和维护测试代码。建议采用如下目录布局[^1]: - `./tests`:放置所有的测试脚本。 - `./data`:用于存储测试所需的数据集(可以是 JSON 文件或 Excel 表格)。 - `./config`:配置文件所在位置。 - `./reports`:保存生成的测试报告。 #### 编写测试案例 下面是一个简单的例子展示如何利用 Requests 库发起 HTTP 请求并验证响应结果: ```python import requests def test_get_user_info(): url = "https://api.example.com/user" response = requests.get(url) assert response.status_code == 200, f"Unexpected status code {response.status_code}" user_data = response.json() assert 'id' in user_data and isinstance(user_data['id'], int), "User ID should be an integer." ``` 此函数会向指定 URL 发送 GET 请求,并检查返回的状态码以及用户信息中的特定字段是否存在且类型正确。 #### 配置 Pytest 执行命令 定义入口文件 main_run.py 来集中控制整个项目的执行流程[^2]: ```python import os import pytest def run_tests(): pytest.main([ '-v', './tests/', # 指定要扫描的测试用例路径 '--alluredir=./results', # 设置Allure的结果输出目录 '--clean-alluredir' # 清理之前的测试记录 ]) os.system('allure serve results') # 启动本地服务查看最新一次的测试报告 if __name__ == "__main__": run_tests() ``` 这段代码设置了 Pytest 的参数选项,指定了测试用例的位置、Allure 报告的存放地点,并清理旧有的测试数据;最后调用了 `os.system()` 方法启动了一个临时服务器以便即时预览 HTML 版本的测试报告。 #### 数据驱动测试 (可选) 如果希望进一步增强灵活性,则可以在测试过程中引入外部数据源。例如,在 YAML 或者 Excel 中准备一组或多组输入值及其预期输出,再通过编程方式读取它们参与实际测试过程[^3]。 对于 YAML 格式的测试数据管理,可以这样操作: ```python import yaml with open('./data/test_cases.yaml', encoding='utf8') as file: cases = yaml.safe_load(file) @pytest.mark.parametrize("input_params", cases["get_user"]) def test_with_yaml(input_params): ... ``` 这里假设有一个名为 `test_cases.yaml` 的文件包含了多个不同场景下的入参组合,上述方法能够遍历每一个条目完成相应的断言逻辑。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值