import pytest
import os
import requests
import openpyxl
import json
from common.get_excel import get_test_cases, write_var_to_excel, update_excel_with_id
from common.get_path import excel_path
from loguru import logger
from common.assert_function import Assertions
from common.extract import extract_vars
from config.config import TOKEN, BASE_URL
import re
# 文件路径
cwd_path = os.path.dirname(__file__)
# 日志初始化
logger.add('./logs/test.log', encoding='utf-8')
# 断言初始化
assertions = Assertions()
# 公共变量
public_vars = {}
# 增强的变量替换函数
def deep_replace_variables(data, variables):
"""深度替换数据中的变量占位符"""
if isinstance(data, str):
# 使用正则表达式替换所有 ${var} 和 {var} 格式的变量
def replace_match(match):
var_name = match.group(1) # 提取变量名
return str(variables.get(var_name, match.group(0))) # 替换为变量值或保留原样
# 替换 ${var} 格式
data = re.sub(r'\$\{(\w+)\}', replace_match, data)
# 替换 {var} 格式
data = re.sub(r'\{(\w+)\}', replace_match, data)
return data
elif isinstance(data, dict):
return {k: deep_replace_variables(v, variables) for k, v in data.items()}
elif isinstance(data, list):
return [deep_replace_variables(item, variables) for item in data]
else:
return data
# API测试客户端
class APIClient:
def __init__(self, base_url):
self.base_url = base_url
self.session = requests.Session()
self.headers = {
'Content-Type': 'application/json',
'Cookie': TOKEN
}
def request(self, method, url, **kwargs):
"""发送HTTP请求"""
full_url = f"{self.base_url}{url}"
try:
# 应用变量替换到请求参数
if 'json' in kwargs and kwargs['json']:
kwargs['json'] = deep_replace_variables(kwargs['json'], public_vars)
logger.debug(f"最终请求参数: {kwargs.get('json')}")
response = self.session.request(
method, full_url, headers=self.headers, **kwargs
)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as e:
logger.error(f"HTTP错误: {e}")
try:
error_content = response.json()
logger.error(f"错误响应内容: {error_content}")
except:
logger.error(f"错误响应文本: {response.text}")
raise
except json.JSONDecodeError:
logger.error(f"响应不是有效的JSON: {response.text}")
raise
except Exception as e:
logger.error(f"请求异常: {e}")
raise
# 测试类
class TestPromotion:
client = APIClient(BASE_URL)
# 参数化测试方法 - 完全基于Excel数据
@pytest.mark.parametrize("case_data", get_test_cases('促销管理', 'CuXiao', ['001', '002', '003', '004']))
def test_promotion_workflow(self, case_data):
global public_vars
# 打印完整的case_data以便调试
logger.info(f"原始 case_data: {case_data}")
print(f"原始 case_data: {case_data}")
# 检查并构建完整的14元素结构
if isinstance(case_data, (list, tuple)):
# 如果只有4个元素,构建为完整的14元素结构
if len(case_data) == 4:
full_case_data = [
'001', # 用例编号
'促销管理', # 模块
'CuXiao', # 接口名称
'高', # 优先级
case_data[0], # 标题
case_data[1], # URL
'POST', # 请求方式
'JSON', # 参数类型
{}, # 请求头
case_data[2], # 请求参数
'成功', # 预期结果
case_data[3], # 响应预期结果
'', # 实际结果
'' # 变量提取
]
# 如果是14个元素,直接使用
elif len(case_data) >= 14:
full_case_data = case_data
else:
logger.error(f"不支持的测试数据长度: {len(case_data)}")
pytest.fail(f"不支持的测试数据长度: {len(case_data)}")
elif isinstance(case_data, dict):
# 字典格式 - 转换为完整结构
full_case_data = [
case_data.get('用例编号', ''),
case_data.get('模块', ''),
case_data.get('接口名称', ''),
case_data.get('优先级', ''),
case_data.get('标题', ''),
case_data.get('URL', ''),
case_data.get('请求方式', 'POST'),
case_data.get('参数类型', 'JSON'),
case_data.get('请求头', {}),
case_data.get('请求参数', {}),
case_data.get('预期结果', ''),
case_data.get('响应预期结果', ''),
case_data.get('实际结果', ''),
case_data.get('变量提取', '')
]
else:
logger.error(f"无法识别的测试数据格式: {type(case_data)}")
pytest.fail(f"无法识别的测试数据格式: {type(case_data)}")
# 现在我们有完整的14元素结构
logger.info(f"完整 case_data: {full_case_data}")
print(f"完整 case_data: {full_case_data}")
# 解析测试数据
title = full_case_data[4] # 标题在第5个位置(索引4)
url = full_case_data[5] # URL在第6个位置(索引5)
request_method = full_case_data[6] # 请求方式在第7个位置(索引6)
request_params = full_case_data[9] # 请求参数在第10个位置(索引9)
expected_result = full_case_data[11] # 响应预期结果在第12个位置(索引11)
extract_expr = full_case_data[13] # 变量提取在第14个位置(索引13)
logger.info(f"\n{'=' * 50}")
logger.info(f"开始执行测试: {title}")
logger.info(f"URL: {url}")
logger.info(f"请求方式: {request_method}")
logger.info(f"原始请求参数: {request_params}")
logger.info(f"当前公共变量: {public_vars}")
# 在发送请求前应用变量替换
try:
# 如果请求参数是字符串,尝试转换为字典
if isinstance(request_params, str):
request_params = json.loads(request_params)
except json.JSONDecodeError:
logger.warning("请求参数不是有效的JSON字符串,保持原样")
pass
# 深度替换变量
request_params = deep_replace_variables(request_params, public_vars)
logger.info(f"替换后的请求参数: {request_params}")
# 发送请求
try:
response = self.client.request(request_method, url, json=request_params)
logger.info(f"响应数据: {response}")
print(f"\n实际结果>>> {response}")
except Exception as e:
logger.error(f"请求发送失败: {str(e)}")
pytest.fail(f"请求发送失败: {str(e)}")
# 断言响应
try:
assert response.get('code') == 0, f"Expected code 0 but got {response.get('code')}"
assert 'ok' in response.get('msg', '').lower(), f"Expected 'ok' in msg but got {response.get('msg')}"
except AssertionError as e:
logger.error(f"断言失败: {str(e)}")
raise
# 变量提取逻辑 - 如果有提取表达式
if extract_expr:
try:
# 解析提取表达式(JSON格式)
extract_dict = json.loads(extract_expr)
local_vars = extract_vars(response, extract_dict)
if local_vars:
# 更新公共变量
public_vars.update(local_vars)
logger.info(f"提取的变量: {local_vars}")
logger.info(f"更新后的公共变量: {public_vars}")
# 特殊处理ID变量
if 'id' in local_vars:
# 写入Excel
write_var_to_excel('全局公共变量', 2, 2, local_vars['id'])
update_excel_with_id(local_vars['id'])
logger.success(f"成功提取并保存ID: {local_vars['id']}")
except Exception as e:
logger.error(f"变量提取失败: {str(e)}")
logger.info(f"测试 {title} 执行完成\n{'=' * 50}")
if __name__ == '__main__':
pytest.main(['-v', __file__])
报错了,'id': '{id}'不对,提取变量失败,应该是'id': '123'占位符应该是实际值
最新发布