diff --git a/commons/cases.py b/commons/cases.py index 123888b..5debb50 100644 --- a/commons/cases.py +++ b/commons/cases.py @@ -48,7 +48,8 @@ class TestAPI: 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格式 print(yaml_path.stem) setattr(cls, f"{yaml_path.stem}", case_func) # 把pytest格式添加到类中 diff --git a/commons/exchange.py b/commons/exchange.py index 33d0bbb..0c475cd 100644 --- a/commons/exchange.py +++ b/commons/exchange.py @@ -85,7 +85,7 @@ if __name__ == '__main__': # print(mock_resp.text) # print(mock_resp.json()) - exchanger = Exchange(r"E:\PyP\InterfaceAutoTest\extract.yaml") + exchanger = Exchange(r"D:\CNWei\CNW\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/__init__.py b/commons/file_processors/__init__.py new file mode 100644 index 0000000..b525101 --- /dev/null +++ b/commons/file_processors/__init__.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python +# coding=utf-8 + +""" +@author: CNWei +@Software: PyCharm +@contact: t6i888@163.com +@file: __init__.py +@date: 2025/3/4 17:23 +@desc: +""" diff --git a/commons/file_processors/base.py b/commons/file_processors/base.py new file mode 100644 index 0000000..0cf65de --- /dev/null +++ b/commons/file_processors/base.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +# coding=utf-8 + +""" +@author: CNWei +@Software: PyCharm +@contact: t6i888@163.com +@file: base +@date: 2025/3/4 17:23 +@desc: +""" +import abc + + +class BaseFileProcessor(abc.ABC): # 使用 abc 模块定义抽象基类 + """ + 文件处理器的抽象基类。 + 定义了所有子类必须实现的方法。 + """ + + @abc.abstractmethod + def load(self): + """加载.""" + pass + + @abc.abstractmethod + def to_string(self) -> str: + """将文件内容转换为字符串。""" + pass + + @abc.abstractmethod + def to_dict(self, data: str) -> dict: + """将文件内容转换为字典。""" + pass + + @abc.abstractmethod + def save(self, new_filepath=None): + """将数据保存.""" + pass diff --git a/commons/file_processors/yaml_processor.py b/commons/file_processors/yaml_processor.py new file mode 100644 index 0000000..b182f42 --- /dev/null +++ b/commons/file_processors/yaml_processor.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python +# coding=utf-8 + +""" +@author: CNWei +@Software: PyCharm +@contact: t6i888@163.com +@file: yaml_processor +@date: 2025/3/4 17:28 +@desc: +""" +import logging +from typing import Union +from dataclasses import dataclass, asdict, field +from pathlib import Path +import yaml +from base import BaseFileProcessor + +logger = logging.getLogger(__name__) + + +class YamlFile(BaseFileProcessor,dict): + """ + 用于处理 YAML 文件的类,继承自 dict。 + 提供了从文件加载、保存到文件、转换为字符串和从字符串转换的功能, + 并可以直接像字典一样访问 YAML 数据。 + """ + + def __init__(self, filepath: Union[str, Path], data: Union[dict, None] = None): + """ + 初始化 YamlFile 对象。 + + Args: + filepath: YAML 文件的路径 (可以是字符串或 pathlib.Path 对象). + data: 可选的初始数据字典。如果提供,则用该字典初始化 YamlFile。 + 如果不提供,则尝试从 filepath 加载数据。 + """ + super().__init__() # 初始化父类 dict + self.filepath: Path = Path(filepath) # 确保 filepath 是 Path 对象 + if data is not None: + self.update(data) # 如果提供了初始数据,则更新字典 + else: + self.load() # 否则,尝试从文件加载 + + def load(self) -> None: + """ + 从 YAML 文件加载数据并更新字典。 + 如果文件不存在或加载失败,则清空字典并记录警告/错误。 + """ + self.clear() # 清空现有数据 + if self.filepath.exists(): + try: + with open(self.filepath, "r", encoding="utf-8") as f: + loaded_data = yaml.safe_load(f) or {} + self.update(loaded_data) # 使用加载的数据更新字典 + except yaml.YAMLError as e: + logger.error(f"加载 YAML 文件 {self.filepath} 时出错: {e}") + # 保持字典为空 (已在开头 clear) + else: + logger.warning(f"文件 {self.filepath} 不存在, 字典保持为空.") + # 保持字典为空 (已在开头 clear) + + def to_string(self) -> str: + """ + 将字典 (自身) 转换为 YAML 格式的字符串。 + + Returns: + YAML 格式的字符串。 + """ + try: + return yaml.safe_dump( + dict(self), # 使用dict转换为标准的字典 + allow_unicode=True, + sort_keys=False, + default_flow_style=False + ) + except TypeError as e: + logger.error(f"将数据转换为 YAML 字符串时出错: {e}") + return "" + + def to_dict(self, data: str) -> None: + """ + 将 YAML 格式的字符串转换为字典,并更新当前字典的内容. + + Args: + data: YAML 格式的字符串。 + """ + try: + loaded_data = yaml.safe_load(data) or {} + self.clear() + self.update(loaded_data) # 清空并更新 + except yaml.YAMLError as e: + logger.error(f"将 YAML 字符串转换为字典时出错: {e}") + self.clear() # 出错时也清空 + + def save(self, new_filepath: Union[str, Path, None] = None): + """ + 将字典数据 (自身) 保存到 YAML 文件。 + + Args: + new_filepath: 可选参数,指定新的文件路径。如果为 None,则覆盖原文件。 + """ + filepath = Path(new_filepath) if new_filepath else self.filepath + + try: + with open(filepath, "w", encoding="utf-8") as f: + yaml.safe_dump( + dict(self), # 使用dict转换为标准的字典 + stream=f, + allow_unicode=True, + sort_keys=False, + default_flow_style=False + ) + except (TypeError, OSError) as e: + logger.error(f"保存 YAML 文件 {filepath} 时出错: {e}") + + +if __name__ == '__main__': + # 示例用法 + yaml_path = r'D:\CNWei\CNW\InterfaceAutoTest\TestCases\test_1_user.yaml' # 你的 YAML 文件路径 + yaml_file = YamlFile(yaml_path) + print(yaml_file) + print(type(yaml_file)) + + # # 直接像字典一样访问数据 + # print("加载的数据:", yaml_file) # 直接打印对象,就是打印字典内容 + # print("title:", yaml_file.get("title")) # 使用 get 方法 + # if "title" in yaml_file: # 使用 in 检查键 + # print("原始title:", yaml_file["title"]) # 使用方括号访问 + # yaml_file["title"] = "新的标题" # 使用方括号修改 + # print("修改后的title:", yaml_file["title"]) + # # + # yaml_file["new_key"] = "new_value" # 添加新的键值对 + # + # # 将字典转换为 YAML 字符串 + # yaml_string = yaml_file.to_string() + # print("\nYAML 字符串:", yaml_string) + # # + # # 将 YAML 字符串转换回字典 (并更新 yaml_file) + # yaml_file.to_dict(yaml_string) + # print("\n从字符串加载的数据:", yaml_file) + # + # # 保存修改后的数据 (覆盖原文件) + # yaml_file.save() + # + # # 保存到新文件 + # new_yaml_path = r'D:\CNWei\CNW\InterfaceAutoTest\TestCases\test_1_user_new.yaml' + # yaml_file.save(new_filepath=new_yaml_path) + + # 测试从字符串初始化 + # yaml_string2 = """ + # name: Test User + # age: 30 + # """ + + # yaml_file2 = YamlFile("test2.yaml", data=yaml.safe_load(yaml_string2)) # 从字符串初始化 + # print("\n从字符串初始化的 YamlFile:", yaml_file2) + # yaml_file2.save() # 保存到 test2.yaml + # + # 测试文件不存在的情形 + # non_existent_file = YamlFile("non_existent_file.yaml") + # print("\n加载不存在的文件:", non_existent_file) # 应该打印空字典 {} + # non_existent_file['a'] = 1 # 可以直接添加 + # print("\n加载不存在的文件:", non_existent_file) + +# if __name__ == '__main__': +# from commons.models import CaseInfo +# +# yaml_path = r'D:\CNWei\CNW\InterfaceAutoTest\TestCases\test_1_user.yaml' +# yaml_file = YamlFile(yaml_path) +# print(yaml_file.load()) +# # yaml_file.load() +# # case_info = CaseInfo(**yaml_file) +# # print(case_info) +# # yaml_file["title"] = "查询用户信息" +# # yaml_file.save() diff --git a/commons/files.py b/commons/files.py index e48e3fa..ef7fafc 100644 --- a/commons/files.py +++ b/commons/files.py @@ -10,7 +10,8 @@ @desc: 读取和保存yaml文件 """ import logging - +from dataclasses import dataclass, asdict, field +from pathlib import Path import yaml logger = logging.getLogger(__name__) @@ -18,24 +19,42 @@ logger = logging.getLogger(__name__) class YamlFile(dict): def __init__(self, path): - super().__init__() - self.path = path - self.load() + super().__init__() # 初始化父类 dict + self.path = Path(path) + self.load() # 链式初始化加载 def load(self): - with open(self.path, "r", encoding="utf-8") as f: - data = yaml.safe_load(f) # 字典 - if data: - self.update(data) # 把两个字段的内容合并 + if self.path.exists(): + with open(self.path, "r", encoding="utf-8") as f: + data = yaml.safe_load(f) or {} # 加载数据,空文件返回空字典 + self.clear() # 清空当前实例 + self.update(data) # 更新字典内容 + else: + logger.warning(f"File {self.path} not found, initialized empty.") + return self # 链式调用 + + def to_yaml(self) -> str: + return yaml.safe_dump( + dict(self), + allow_unicode=True, + sort_keys=False + ) + + + @classmethod + def by_yaml(cls, yaml_str): + data = yaml.safe_load(yaml_str) or {} + return cls({**data}) # 通过类方法创建实例 def save(self): with open(self.path, "w", encoding="utf-8") as f: yaml.safe_dump( - dict(self), + dict(self), # 直接 dump 实例本身(已继承 dict) stream=f, - allow_unicode=True, # allow_unicode:使用unicode编码正常显示中文 - sort_keys=False # sort_keys:保持原有排序 + allow_unicode=True, + sort_keys=False ) + return self # 链式调用 if __name__ == '__main__': diff --git a/commons/funcs.py b/commons/funcs.py index 167fdae..13924be 100644 --- a/commons/funcs.py +++ b/commons/funcs.py @@ -15,7 +15,7 @@ import time import urllib.parse import hashlib -from commons.databases import db +# from commons.databases import db from commons.files import YamlFile from commons import settings diff --git a/extract.yaml b/extract.yaml index 7fd8aef..862d6c6 100644 --- a/extract.yaml +++ b/extract.yaml @@ -1,2 +1,7 @@ -msg: Success. -id: 12 +name: 张三 +age: '18' +data: +- 3 +- 4 +- 5 +aaa: null diff --git a/logs/pytest.log b/logs/pytest.log index 84f0002..a70ffbe 100644 --- a/logs/pytest.log +++ b/logs/pytest.log @@ -1,5 +1,5 @@ -03/02/2025 09:38:14 PM [commons.cases] INFO cases.find_yaml_case:45 - 加载文件:E:\PyP\InterfaceAutoTest\TestCases\answer\test_1_status.yaml -03/02/2025 09:38:14 PM [commons.cases] INFO cases.find_yaml_case:50 - case_info=title: 查询状态信息 +03/03/2025 05:34:28 PM [commons.cases] INFO cases.find_yaml_case:45 - 加载文件:D:\CNWei\CNW\InterfaceAutoTest\TestCases\answer\test_1_status.yaml +03/03/2025 05:34:28 PM [commons.cases] INFO cases.find_yaml_case:50 - case_info=title: 查询状态信息 request: method: get url: /answer/api/v1/connector/info @@ -26,29 +26,29 @@ epic: 项目名称:answer feature: 页面状态 story: 状态 -03/02/2025 09:38:14 PM [commons.models] INFO models.ddt:81 - 1,执行这一步 -03/02/2025 09:38:14 PM [commons.cases] INFO cases.new_case:62 - ddt_title=['查询状态信息'] -03/02/2025 09:38:14 PM [pytest_result_log] INFO plugin.pytest_runtest_setup:122 - -----------------Start: main.py::TestAPI::test_1_status[查询状态信息]----------------- -03/02/2025 09:38:14 PM [commons.cases] INFO cases.test_func:70 - =================================用例开始执行:查询状态信息================================== -03/02/2025 09:38:14 PM [commons.exchange] INFO exchange.replace:64 - CaseInfo(title='查询状态信息', request={'method': 'get', 'url': '/answer/api/v1/connector/info', 'headers': {'Host': '119.91.19.171:40065', 'Accept-Language': 'en_US', 'Accept': 'application/json, text/plain, */*', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0', 'Referer': 'http://119.91.19.171:40065/users/login', 'Accept-Encoding': 'gzip, deflate'}}, extract={'msg': ['json', '$.msg', 0]}, validate={'equals': {'状态码等于200': ['Success.', '${msg}']}}, parametrize=[], epic='项目名称:answer', feature='页面状态', story='状态') -03/02/2025 09:38:14 PM [commons.templates] INFO templates.safe_substitute_funcs:51 - mapping更新前: {'msg': 'Success.', 'id': 12} -03/02/2025 09:38:14 PM [commons.templates] INFO templates.safe_substitute_funcs:54 - mapping更新后: {'msg': 'Success.', 'id': 12, 'int': , 'float': , 'bool': , 'url_unquote': , 'str': , 'time_str': , 'add': , 'sql': , 'new_id': , 'last_id': , 'md5': , 'base64_encode': , 'base64_decode': , 'rsa_encode': , 'rsa_decode': } -03/02/2025 09:38:14 PM [commons.cases] INFO cases.test_func:74 - 1,正在注入变量... -03/02/2025 09:38:14 PM [commons.cases] INFO cases.test_func:77 - 2,正在请求接口... -03/02/2025 09:38:14 PM [requests.session] INFO session.send:36 - 发送请求>>>>>> 接口地址 = GET http://119.91.19.171:40065/answer/api/v1/connector/info -03/02/2025 09:38:14 PM [requests.session] INFO session.send:37 - 发送请求>>>>>> 请求头 = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': 'application/json, text/plain, */*', 'Connection': 'keep-alive', 'Host': '119.91.19.171:40065', 'Accept-Language': 'en_US', 'Referer': 'http://119.91.19.171:40065/users/login'} -03/02/2025 09:38:14 PM [requests.session] INFO session.send:38 - 发送请求>>>>>> 请求正文 = None -03/02/2025 09:38:14 PM [requests.session] INFO session.send:42 - 接收响应 <<<<<< 状态码 = 200 -03/02/2025 09:38:14 PM [requests.session] INFO session.send:43 - 接收响应 <<<<<< 响应头 = {'Content-Type': 'application/json; charset=utf-8', 'Date': 'Sun, 02 Mar 2025 13:38:14 GMT', 'Content-Length': '63'} -03/02/2025 09:38:14 PM [requests.session] INFO session.send:44 - 接收响应 <<<<<< 响应正文 = {'code': 200, 'reason': 'base.success', 'msg': 'Success.', 'data': []} -03/02/2025 09:38:14 PM [commons.cases] INFO cases.test_func:80 - 3,正在提取变量... -03/02/2025 09:38:14 PM [commons.cases] INFO cases.test_func:86 - 4,正在断言... -03/02/2025 09:38:14 PM [commons.exchange] INFO exchange.replace:64 - CaseInfo(title='查询状态信息', request={'method': 'get', 'url': '/answer/api/v1/connector/info', 'headers': {'Host': '119.91.19.171:40065', 'Accept-Language': 'en_US', 'Accept': 'application/json, text/plain, */*', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0', 'Referer': 'http://119.91.19.171:40065/users/login', 'Accept-Encoding': 'gzip, deflate'}}, extract={'msg': ['json', '$.msg', 0]}, validate={'equals': {'状态码等于200': ['Success.', '${msg}']}}, parametrize=[], epic='项目名称:answer', feature='页面状态', story='状态') -03/02/2025 09:38:14 PM [commons.templates] INFO templates.safe_substitute_funcs:51 - mapping更新前: {'msg': 'Success.', 'id': 12} -03/02/2025 09:38:14 PM [commons.templates] INFO templates.safe_substitute_funcs:54 - mapping更新后: {'msg': 'Success.', 'id': 12, 'int': , 'float': , 'bool': , 'url_unquote': , 'str': , 'time_str': , 'add': , 'sql': , 'new_id': , 'last_id': , 'md5': , 'base64_encode': , 'base64_decode': , 'rsa_encode': , 'rsa_decode': } -03/02/2025 09:38:14 PM [utils.case_validator] INFO case_validator.assert_all:32 - 键:equals,值:{'状态码等于200': ['Success.', 'Success.']} -03/02/2025 09:38:14 PM [utils.case_validator] INFO case_validator.assert_all:34 - 获取到的断言: -03/02/2025 09:38:14 PM [utils.case_validator] INFO case_validator.validate_equals:43 - assert Success. == Success., 状态码等于200执行这段代码 -03/02/2025 09:38:14 PM [commons.cases] INFO cases.test_func:91 - =================================用例执行结束:查询状态信息================================== -03/02/2025 09:38:14 PM [pytest_result_log] INFO plugin.pytest_result_log:190 - test status is PASSED (main.py::TestAPI::test_1_status[查询状态信息]): -03/02/2025 09:38:14 PM [pytest_result_log] INFO plugin.pytest_runtest_teardown:128 - ------------------End: main.py::TestAPI::test_1_status[查询状态信息]------------------ +03/03/2025 05:34:28 PM [commons.models] INFO models.ddt:81 - 1,执行这一步 +03/03/2025 05:34:28 PM [commons.cases] INFO cases.new_case:63 - ddt_title=['查询状态信息'] +03/03/2025 05:34:28 PM [pytest_result_log] INFO plugin.pytest_runtest_setup:122 - -----------------Start: main.py::TestAPI::test_1_status[查询状态信息]----------------- +03/03/2025 05:34:28 PM [commons.cases] INFO cases.test_func:71 - =================================用例开始执行:查询状态信息================================== +03/03/2025 05:34:28 PM [commons.exchange] INFO exchange.replace:64 - CaseInfo(title='查询状态信息', request={'method': 'get', 'url': '/answer/api/v1/connector/info', 'headers': {'Host': '119.91.19.171:40065', 'Accept-Language': 'en_US', 'Accept': 'application/json, text/plain, */*', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0', 'Referer': 'http://119.91.19.171:40065/users/login', 'Accept-Encoding': 'gzip, deflate'}}, extract={'msg': ['json', '$.msg', 0]}, validate={'equals': {'状态码等于200': ['Success.', '${msg}']}}, parametrize=[], epic='项目名称:answer', feature='页面状态', story='状态') +03/03/2025 05:34:28 PM [commons.templates] INFO templates.safe_substitute_funcs:51 - mapping更新前: {'msg': 'Success.', 'id': 12} +03/03/2025 05:34:28 PM [commons.templates] INFO templates.safe_substitute_funcs:54 - mapping更新后: {'msg': 'Success.', 'id': 12, 'int': , 'float': , 'bool': , 'url_unquote': , 'str': , 'time_str': , 'add': , 'sql': , 'new_id': , 'last_id': , 'md5': , 'base64_encode': , 'base64_decode': , 'rsa_encode': , 'rsa_decode': } +03/03/2025 05:34:28 PM [commons.cases] INFO cases.test_func:75 - 1,正在注入变量... +03/03/2025 05:34:28 PM [commons.cases] INFO cases.test_func:78 - 2,正在请求接口... +03/03/2025 05:34:28 PM [requests.session] INFO session.send:36 - 发送请求>>>>>> 接口地址 = GET http://119.91.19.171:40065/answer/api/v1/connector/info +03/03/2025 05:34:28 PM [requests.session] INFO session.send:37 - 发送请求>>>>>> 请求头 = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': 'application/json, text/plain, */*', 'Connection': 'keep-alive', 'Host': '119.91.19.171:40065', 'Accept-Language': 'en_US', 'Referer': 'http://119.91.19.171:40065/users/login'} +03/03/2025 05:34:28 PM [requests.session] INFO session.send:38 - 发送请求>>>>>> 请求正文 = None +03/03/2025 05:34:28 PM [requests.session] INFO session.send:42 - 接收响应 <<<<<< 状态码 = 200 +03/03/2025 05:34:28 PM [requests.session] INFO session.send:43 - 接收响应 <<<<<< 响应头 = {'Content-Type': 'application/json; charset=utf-8', 'Date': 'Mon, 03 Mar 2025 09:34:29 GMT', 'Content-Length': '63'} +03/03/2025 05:34:28 PM [requests.session] INFO session.send:44 - 接收响应 <<<<<< 响应正文 = {'code': 200, 'reason': 'base.success', 'msg': 'Success.', 'data': []} +03/03/2025 05:34:28 PM [commons.cases] INFO cases.test_func:81 - 3,正在提取变量... +03/03/2025 05:34:28 PM [commons.cases] INFO cases.test_func:87 - 4,正在断言... +03/03/2025 05:34:28 PM [commons.exchange] INFO exchange.replace:64 - CaseInfo(title='查询状态信息', request={'method': 'get', 'url': '/answer/api/v1/connector/info', 'headers': {'Host': '119.91.19.171:40065', 'Accept-Language': 'en_US', 'Accept': 'application/json, text/plain, */*', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0', 'Referer': 'http://119.91.19.171:40065/users/login', 'Accept-Encoding': 'gzip, deflate'}}, extract={'msg': ['json', '$.msg', 0]}, validate={'equals': {'状态码等于200': ['Success.', '${msg}']}}, parametrize=[], epic='项目名称:answer', feature='页面状态', story='状态') +03/03/2025 05:34:28 PM [commons.templates] INFO templates.safe_substitute_funcs:51 - mapping更新前: {'msg': 'Success.', 'id': 12} +03/03/2025 05:34:28 PM [commons.templates] INFO templates.safe_substitute_funcs:54 - mapping更新后: {'msg': 'Success.', 'id': 12, 'int': , 'float': , 'bool': , 'url_unquote': , 'str': , 'time_str': , 'add': , 'sql': , 'new_id': , 'last_id': , 'md5': , 'base64_encode': , 'base64_decode': , 'rsa_encode': , 'rsa_decode': } +03/03/2025 05:34:28 PM [utils.case_validator] INFO case_validator.assert_all:32 - 键:equals,值:{'状态码等于200': ['Success.', 'Success.']} +03/03/2025 05:34:28 PM [utils.case_validator] INFO case_validator.assert_all:34 - 获取到的断言: +03/03/2025 05:34:28 PM [utils.case_validator] INFO case_validator.validate_equals:43 - assert Success. == Success., 状态码等于200执行这段代码 +03/03/2025 05:34:28 PM [commons.cases] INFO cases.test_func:92 - =================================用例执行结束:查询状态信息================================== +03/03/2025 05:34:28 PM [pytest_result_log] INFO plugin.pytest_result_log:190 - test status is PASSED (main.py::TestAPI::test_1_status[查询状态信息]): +03/03/2025 05:34:28 PM [pytest_result_log] INFO plugin.pytest_runtest_teardown:128 - ------------------End: main.py::TestAPI::test_1_status[查询状态信息]------------------ diff --git a/utils/case_parser.py b/utils/case_parser.py index a08f8bd..6c270c6 100644 --- a/utils/case_parser.py +++ b/utils/case_parser.py @@ -21,6 +21,12 @@ from commons.models import CaseInfo class CaseParser: @staticmethod def to_yaml(case_data: dict) -> str: + try: + + CaseInfo(**case_data) + except TypeError as error: + logging.error(error) + raise error return yaml.safe_dump(case_data, allow_unicode=True, sort_keys=False) @staticmethod @@ -38,5 +44,5 @@ if __name__ == '__main__': case_parser = CaseParser() case_data_ = case_parser.to_yaml(data) # print(case_data_) - case_parser.from_yaml(case_data_) + # case_parser.from_yaml(case_data_) # print(type(case_data_)) diff --git a/utils/data_driver.py b/utils/data_driver.py new file mode 100644 index 0000000..dfe0b20 --- /dev/null +++ b/utils/data_driver.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# coding=utf-8 + +""" +@author: CNWei +@Software: PyCharm +@contact: t6i888@163.com +@file: data_driver +@date: 2025/3/3 10:56 +@desc: +""" + +from commons.models import CaseInfo +from commons.templates import Template + + +class DataDriver: + @staticmethod + def generate_cases(case_info) -> list: + if not case_info.get("parametrize"): + return [case_info] + + cases = [] + args_names = case_info.get("parametrize")[0] + for args_values in 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 + + +if __name__ == '__main__': + mock_case_info = { + "case_info0": { + "feature": "页面状态", + "story": "状态", + "title": "查询状态信息", + "request": "", + "extract": "", + "validate": "", + "parametrize": [[ "title","username","password","msg" ] ,[ "测试1","user1","pass1","200" ] ,[ "测试2","user2","pass2","300" ]] + }, + "case_info1": { + "feature": "页面状态", + "story": "状态", + "title": "查询状态信息", + "request": "", + "extract": "", + "validate": "", + "parametrize": [1,2,3] + }, + "case_info2": { + "feature": "页面状态", + "story": "状态", + "title": "查询状态信息", + "request": "", + "extract": "", + "validate": "", + "parametrize": [1,2,3] + } + + } + + dd = DataDriver() + cases = dd.generate_cases(mock_case_info.get("case_info0")) + print(cases) \ No newline at end of file