From 31fad3f4e1982994cfa52cd9450f7a3fcf451576 Mon Sep 17 00:00:00 2001 From: CNWei Date: Thu, 6 Mar 2025 00:26:43 +0800 Subject: [PATCH] =?UTF-8?q?refactor(cases,yaml=5Fprocessor):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E4=BB=A5=E5=8F=8A=E6=96=87=E4=BB=B6=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 优化用例加载模块器 - 优化yaml文件读取模块 --- commons/cases.py | 49 ++++++++++++------ commons/exchange.py | 4 +- commons/file_processors/yaml_processor.py | 39 +++++++++++++- utils/data_driver.py | 63 +++++++++++++++++------ 4 files changed, 121 insertions(+), 34 deletions(-) diff --git a/commons/cases.py b/commons/cases.py index 5debb50..6250c01 100644 --- a/commons/cases.py +++ b/commons/cases.py @@ -16,11 +16,11 @@ import allure import pytest from commons import settings -from commons.files import YamlFile +from commons.file_processors.yaml_processor import YamlFile from commons.models import CaseInfo from commons.session import Session from commons.exchange import Exchange - +from utils import data_driver logger = logging.getLogger(__name__) @@ -45,28 +45,47 @@ class TestAPI: logger.info(f"加载文件:{yaml_path}") file = YamlFile(yaml_path) # 自动读取yaml文件 + case_info = CaseInfo(**file) # 校验yaml格式 logger.info(f"case_info={case_info.to_yaml()}") # 把case_info 转成字符串,然后记录日志 # case_info = {yaml_path.stem:case_info} # logger.info(f"case_info_dict={case_info}") - case_func = cls.new_case(case_info) # 从yaml格式转换为pytest格式 + case_func = cls.new_case(yaml_path.stem, file) # 从yaml格式转换为pytest格式 print(yaml_path.stem) setattr(cls, f"{yaml_path.stem}", case_func) # 把pytest格式添加到类中 @classmethod - def new_case(cls, case_info: CaseInfo): - ddt_data = case_info.ddt() - print(f"测试数据:{ddt_data}") - - ddt_title = [data.title for data in ddt_data] - logger.info(f"{ddt_title=}") - - @allure.feature(case_info.feature) - @allure.story(case_info.story) - @pytest.mark.parametrize("case_info", ddt_data, ids=ddt_title) - def test_func(self, case_info: CaseInfo): - allure.dynamic.title(case_info.title) + def new_case(cls,file_name, case_info: dict): + case_data = data_driver.DataDriver().generate_cases(file_name,case_info) + # ddt_data = case_info.ddt() + keys_list = ['test_1_user[0]', 'test_1_user[1]', 'test_1_user[2]', 'test_1_user[3]'] + titles = ['查询用户信息', '查询用户信息', '查询用户信息', '查询用户信息'] + for item in case_data: + # 遍历列表中的每个字典 + for key, value in item.items(): + print(f"key:{key}") + keys_list.append(key) + print(f"value:{value}") + # # 遍历内层字典(这里内层字典其实只有一个键值对) + titles.append(value['title']) + print(f"测试数据:{case_data}") + item={'test_1_user[0]': {'feature': '特征', 'story': '事件', 'title': '查询用户信息', 'request': {'method': 'get', 'url': 'http://119.91.19.171:40065/answer/api/v1/connector/info', 'headers': {'Accept-Encoding': 'gzip, deflate', 'Accept-Language': 'zh_CN', 'Content-Type': 'application/json', 'Cookie': 'psession=33c6c2de-7e5d-40e2-9bbc-3c637a690c3f; lang=zh-CN; 3x-ui=MTcyNjU2NDcwOHxEWDhFQVFMX2dBQUJFQUVRQUFCMV80QUFBUVp6ZEhKcGJtY01EQUFLVEU5SFNVNWZWVk5GVWhoNExYVnBMMlJoZEdGaVlYTmxMMjF2WkdWc0xsVnpaWExfZ1FNQkFRUlZjMlZ5QWYtQ0FBRUVBUUpKWkFFRUFBRUlWWE5sY201aGJXVUJEQUFCQ0ZCaGMzTjNiM0prQVF3QUFRdE1iMmRwYmxObFkzSmxkQUVNQUFBQUdQLUNGUUVDQVFkNGRXa3lNREkwQVFkNGRXa3lNREkwQUE9PXwLOhLRIDjzvQ3oI-UF-GhkMheEENkxRJ8GkAZ79eFHvg==', 'Host': '119.91.19.171:40065', 'Origin': 'http://119.91.19.171:40065', 'Referer': 'http://119.91.19.171:40065/users/login', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0'}}, 'extract': {'code': ['json', '$.code', 0], 'msg': ['json', '$.msg', 0]}, 'validate': {'equals': {'状态码等于200': [200, 'code1']}, 'not_equals': {'状态码不等于404': [404, 'code1']}, 'contains': {'包含关系': [404, 'code1']}, 'not_contains': {'不包含关系': [404, 'code1']}}, 'parametrize': [['title', 'username', 'password', 'code'], ['测试1', 'user1', 'pass1', 'code1'], ['测试2', 'user2', 'pass2', 'code2'], ['测试3', 'user3', 'pass3', 'code3'], ['测试4', 'user4', 'pass4', 'code4']]}} + # ddt_title = [data.title for data in ddt_data] + # logger.info(f"{ddt_title=}") + logger.info(f"keys_list={keys_list}") + logger.info(f"titles={titles}") + logger.info(f"feature={case_info.get('feature')}") + logger.info(f"story={case_info.get('story')}") + @allure.feature(case_info.get("feature")) + @allure.story(case_info.get("story")) + @pytest.mark.parametrize("case_key", keys_list, ids=titles) + def test_func(self, case_key): + logger.info(f"case_key={case_key}") + item.get(case_key) + logger.info(f"========:{item}") + logger.info(f"========:{item.get(case_key)}") + # allure.dynamic.title(case_info.title) logger.info(f"用例开始执行:{case_info.title}".center(80, "=")) diff --git a/commons/exchange.py b/commons/exchange.py index 0c475cd..85b9e0d 100644 --- a/commons/exchange.py +++ b/commons/exchange.py @@ -19,7 +19,7 @@ import allure from commons.templates import Template import jsonpath -from commons.files import YamlFile +from commons.file_processors.yaml_processor import YamlFile from commons.models import CaseInfo logger = logging.getLogger(__name__) @@ -85,7 +85,7 @@ if __name__ == '__main__': # print(mock_resp.text) # print(mock_resp.json()) - exchanger = Exchange(r"D:\CNWei\CNW\InterfaceAutoTest\extract.yaml") + exchanger = Exchange(r"E:\PyP\InterfaceAutoTest\extract.yaml") exchanger.extract(mock_resp, "name", "json", '$.name', 0) exchanger.extract(mock_resp, "age", "json", '$.age', 0) exchanger.extract(mock_resp, "data", "json", '$.data', 0) diff --git a/commons/file_processors/yaml_processor.py b/commons/file_processors/yaml_processor.py index b182f42..9e2e74b 100644 --- a/commons/file_processors/yaml_processor.py +++ b/commons/file_processors/yaml_processor.py @@ -14,12 +14,12 @@ from typing import Union from dataclasses import dataclass, asdict, field from pathlib import Path import yaml -from base import BaseFileProcessor +from commons.file_processors.base import BaseFileProcessor logger = logging.getLogger(__name__) -class YamlFile(BaseFileProcessor,dict): +class YamlFile(BaseFileProcessor, dict): """ 用于处理 YAML 文件的类,继承自 dict。 提供了从文件加载、保存到文件、转换为字符串和从字符串转换的功能, @@ -115,6 +115,41 @@ class YamlFile(BaseFileProcessor,dict): logger.error(f"保存 YAML 文件 {filepath} 时出错: {e}") +class StringOrDict: + @classmethod + def to_string(cls, data: dict) -> str: + """ + 将字典 (自身) 转换为 YAML 格式的字符串。 + + Returns: + YAML 格式的字符串。 + """ + try: + return yaml.safe_dump( + dict(data), # 使用dict转换为标准的字典 + allow_unicode=True, + sort_keys=False, + default_flow_style=False + ) + except TypeError as e: + logger.error(f"将数据转换为 YAML 字符串时出错: {e}") + return "" + + @classmethod + def to_dict(cls, data: str) -> None: + """ + 将 YAML 格式的字符串转换为字典,并更新当前字典的内容. + + Args: + data: YAML 格式的字符串。 + """ + try: + loaded_data = yaml.safe_load(data) or {} + return loaded_data + except yaml.YAMLError as e: + logger.error(f"将 YAML 字符串转换为字典时出错: {e}") + + if __name__ == '__main__': # 示例用法 yaml_path = r'D:\CNWei\CNW\InterfaceAutoTest\TestCases\test_1_user.yaml' # 你的 YAML 文件路径 diff --git a/utils/data_driver.py b/utils/data_driver.py index dfe0b20..801cd8c 100644 --- a/utils/data_driver.py +++ b/utils/data_driver.py @@ -9,29 +9,41 @@ @date: 2025/3/3 10:56 @desc: """ +from pathlib import Path from commons.models import CaseInfo from commons.templates import Template +from commons.file_processors.yaml_processor import StringOrDict class DataDriver: @staticmethod - def generate_cases(case_info) -> list: - if not case_info.get("parametrize"): - return [case_info] + def generate_cases(file_name, case_info) -> list: - cases = [] + if not case_info.get("parametrize"): + return {file_name + "[--]": case_info} + + # cases = [] args_names = case_info.get("parametrize")[0] - for args_values in case_info.get("parametrize")[1:]: - print(args_values) + for i, args_values in enumerate(case_info.get("parametrize")[1:]): + # print(args_values) context = dict(zip(args_names, args_values)) - print(context) - rendered = Template(CaseInfo(**case_info).to_yaml()).render(context) - cases.append({args_names[0]:CaseInfo(**case_info).by_yaml(rendered)}) - return cases + # print(context) + # rendered = Template(CaseInfo(**case_info).to_yaml()).render(context) + rendered = Template(StringOrDict.to_string(case_info)).render(context) + # cases.append({file_name + "[" + str(i) + "]": StringOrDict.to_dict(rendered)}) + yield {file_name + "[" + str(i) + "]": StringOrDict.to_dict(rendered)} + # return cases if __name__ == '__main__': + from commons.file_processors.yaml_processor import YamlFile + + file_path = Path(r"E:\PyP\InterfaceAutoTest\TestCases\test_1_user.yaml") + + file_obj = YamlFile(file_path) + # print(file_path.stem) + file_name = file_path.stem mock_case_info = { "case_info0": { "feature": "页面状态", @@ -40,7 +52,8 @@ if __name__ == '__main__': "request": "", "extract": "", "validate": "", - "parametrize": [[ "title","username","password","msg" ] ,[ "测试1","user1","pass1","200" ] ,[ "测试2","user2","pass2","300" ]] + "parametrize": [["title", "username", "password", "msg"], ["测试1", "user1", "pass1", "200"], + ["测试2", "user2", "pass2", "300"]] }, "case_info1": { "feature": "页面状态", @@ -49,7 +62,7 @@ if __name__ == '__main__': "request": "", "extract": "", "validate": "", - "parametrize": [1,2,3] + "parametrize": [1, 2, 3] }, "case_info2": { "feature": "页面状态", @@ -58,11 +71,31 @@ if __name__ == '__main__': "request": "", "extract": "", "validate": "", - "parametrize": [1,2,3] + "parametrize": [1, 2, 3] } } dd = DataDriver() - cases = dd.generate_cases(mock_case_info.get("case_info0")) - print(cases) \ No newline at end of file + # cases = dd.generate_cases(mock_case_info.get("case_info0")) + cases = dd.generate_cases(file_name, file_obj) + # print(cases) + # print(len(cases)) + keys_list = [] + titles = [] + for item in cases: + print(item) + # 遍历列表中的每个字典 + for key, value in item.items(): + print(f"key:{key}") + keys_list.append(key) + print(f"value:{value}") + # # 遍历内层字典(这里内层字典其实只有一个键值对) + titles.append(value['title']) + print(item) + + print(keys_list) + print(titles) + + # ddt_title = [data.title for data in ddt_data] + # logger.info(f"{ddt_title=}")