diff --git a/custom_utils/assertion_utils/assert_control.py b/custom_utils/assertion_utils/assert_control.py index 54ca84c..45a4302 100644 --- a/custom_utils/assertion_utils/assert_control.py +++ b/custom_utils/assertion_utils/assert_control.py @@ -129,7 +129,9 @@ class AssertUtils: f"assert_type: {assert_type}\n" f"expect_value: {expect_value}\n" f"actual_value: {actual_value}\n") - message = message or f"断言 --> 预期结果:{type(expect_value)} || {expect_value} 实际结果:{type(actual_value)} || {actual_value}" + message = message or (f"断言 --> " + f"预期结果:{type(expect_value)} || {expect_value}" + f"实际结果:{type(actual_value)} || {actual_value}") with allure.step(message): # 调用utils.assertion_utils.assert_type里面的方法 self.assert_function_mapping[assert_type](expect_value=expect_value, actual_value=actual_value, diff --git a/custom_utils/case_generate_utils/case_data_analysis.py b/custom_utils/case_generate_utils/case_data_analysis.py index 9f112d3..73e1748 100644 --- a/custom_utils/case_generate_utils/case_data_analysis.py +++ b/custom_utils/case_generate_utils/case_data_analysis.py @@ -8,8 +8,10 @@ # 标准库导入 from typing import Text # 第三方库导入 -from custom_utils.models import TestCaseEnum, Method, RequestType, Severity from loguru import logger +# 本地应用/模块导入 +from custom_utils.models import TestCaseEnum, Method, RequestType, Severity + class CaseCheckException(Exception): """用例检查异常类""" @@ -91,7 +93,8 @@ class CaseDataCheck: logger.error(error_msg) raise CaseCheckException(self.case_id, error_msg) except Exception as e: - error_msg = f"在处理 {field} 时发生异常,需要检查的field_name = {field.value[0]},所有的case_data = {self.case_data},请检查拼写。" + error_msg = (f"在处理 {field} 时发生异常,需要检查的field_name = {field.value[0]}," + f"所有的case_data = {self.case_data},请检查拼写。") raise CaseCheckException(self.case_id, error_msg) def check_params_right(self, enum_name, attr): diff --git a/custom_utils/case_generate_utils/case_fun_generate.py b/custom_utils/case_generate_utils/case_fun_generate.py index 0a67e32..b9b9086 100644 --- a/custom_utils/case_generate_utils/case_fun_generate.py +++ b/custom_utils/case_generate_utils/case_fun_generate.py @@ -7,7 +7,7 @@ # 标准库导入 import os from string import Template -import datetime +from datetime import datetime, timezone import re # 第三方库导入 from loguru import logger @@ -260,7 +260,7 @@ def gen_case_file(filename, case_template_path, config, common_dependence, case_ "func_title": func_name, # 在测试用例中移除了测试类,直接使用测试方法 # "class_title": class_name, - "now": datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + "now": datetime.now(tz=timezone.utc).strftime('%Y-%m-%d %H:%M:%S') } ) else: @@ -295,6 +295,7 @@ def gen_case_file(filename, case_template_path, config, common_dependence, case_ except Exception as e: logger.error(f"处理用例 {filename} 时发生错误: {str(e)}\n用例数据: {case_data}") + def is_valid_marker(markers): """ 检查标记名称是否合法:仅支持非数字/下划线开头,由数字,字母,下划线组成的标记名称 diff --git a/custom_utils/data_utils/data_handle.py b/custom_utils/data_utils/data_handle.py index 3ed996c..4e12d20 100644 --- a/custom_utils/data_utils/data_handle.py +++ b/custom_utils/data_utils/data_handle.py @@ -7,7 +7,8 @@ # 标准库导入 import random # 导包不能移除,否则random.choice这种就不能处理了 import json -import re, uuid +import re +import uuid import copy # 第三方库导入 from string import Template @@ -48,7 +49,8 @@ class DataHandle: 例如: 原字符串:user_id: ${user_id}, user_name: ${user_name} 替换后的原字符串:user_id: e1c6fc74-2f21-49a9-8d0c-de16650c6364, user_name: 50c74155-5cb5-4809-bc5d-277addf8c3e7 - 暂存的需要被处理的关键字或函数:{'e1c6fc74-2f21-49a9-8d0c-de16650c6364': {0: '${user_id}', 1: 'user_id'}, '50c74155-5cb5-4809-bc5d-277addf8c3e7': {0: '${user_name}', 1: 'user_name'}} + 暂存的需要被处理的关键字或函数: + {'e1c6fc74-2f21-49a9-8d0c-de16650c6364': {0: '${user_id}', 1: 'user_id'}, '50c74155-5cb5-4809-bc5d-277addf8c3e7': {0: '${user_name}', 1: 'user_name'}} """ placeholders = {} @@ -182,141 +184,141 @@ class DataHandle: # 声明data_handle方法,这样外部就可以直接import data_handle来使用了 data_handle = DataHandle().data_handle -if __name__ == '__main__': - # 下面是测试代码 - print("\n----------测试场景1: 识别${python表达式},这里random方法是需要导入random包的---------------------\n") - data = "选择.gitignore: ${random.choice(['Ada', 'Actionscript', 'Ansible', 'Android', 'Agda'])},开源许可证: ${random.choice(['0BSD', 'AAL', 'AFL-1.1', '389-exception'])}" - new = data_handle(data) - print(new, type(new), - end="\n\n---------------------------------------------------------------------------------------------\n\n") - - print("-----------测试场景2:识别${python表达式},可以在当前文件导入其他模块,一样可以识别替换---------------------") - # 导入其他方法,也可以直接使用 - # from common_utils.time_handle import test_fun_a - # data = "${test_fun_a()}" +# if __name__ == '__main__': + # # 下面是测试代码 + # print("\n----------测试场景1: 识别${python表达式},这里random方法是需要导入random包的---------------------\n") + # data = "选择.gitignore: ${random.choice(['Ada', 'Actionscript', 'Ansible', 'Android', 'Agda'])},开源许可证: ${random.choice(['0BSD', 'AAL', 'AFL-1.1', '389-exception'])}" # new = data_handle(data) - # print(new, type(new)) - - print("\n-----------测试场景3:识别FakerData类中的方法---------------------\n") - """ - 使用FakerData类中的方法可以直接这样写:${generate_random_int()}, 也可以带上类名:${FakerData().generate_random_int()} - """ - data = { - "age": "${generate_random_int()}", - "message": "Hello, ${FakerData().generate_female_name()}!", - "nested_data": [ - "This is ${name}'s data.", - { - "message": "Age: ${generate_random_int()}", - "nested_list": [ - "More data: ${FakerData().generate_random_int()}", - ] - } - ] - } - new = data_handle(data) - print(new, type(new), end="\n\n") - - """ - 使用FakerData类中的方法, 支持方法传参使用,注意参数如果是str格式,建议使用单引号 - """ - payload = { - "name": "${generate_name(lan='zh')}", - "repository_name": "${generate_name('zh')}", - "desc": '[[1,2,3,4],"${FakerData().generate_random_int()}"]', - "pre": '[[1,2,3,4],${FakerData().generate_name()}]', - "startTime": "${FakerData.generate_time('%Y-%m-%d')}", - } - new = data_handle(payload) - print(new, type(new), end="\n\n") - - """ - 还可以直接使用FakerData类中的实例属性 - """ - - data = { - "payload": { - "en_name": "${faker.name()}", # 这里是使用类FakerData里面的实例属性faker - "zh_name": "${fk_zh.name()}", # 这里是使用类FakerData里面的实例属性fk_zh - "url": "/api/accounts/${FakerData.generate_time('%Y-%m-%d')}/login.json", - } - } - - new = data_handle(data) - print(new, type(new), end="\n\n") - - """ - FakerData类中没有封装random_name这个方法,会无法处理 - """ - data = '[[1,2,3,4],"${FakerData().random_name()}"]' - new = data_handle(data) - print(new, type(new), - end="\n\n---------------------------------------------------------------------------------------------\n\n") - - print("\n-----------测试场景4:识别${}进行关键字替换---------------------\n") - user_info = { - "user_id": 104, - "user_name": "flora" - } - data_03 = "user_id: ${user_id}, user_name: ${user_name}" - new = data_handle(data_03, user_info) - print(new, type(new), end="\n\n") - - """ - 识别${}进行关键字替换时会保留原值的类型。 比如eval('1,2,4')会变成元组(1,2,4)。经过本方法处理,会保留原有格式 - """ - data = { - "winner_id": "${winner_id}", - "user_id": "${user_id}", - "time": "${generate_time()}", - "attachment_ids": "${attachment_ids}", - "assigned_id": "${assigned_id}", - "cookies": "${cookies}" - } - source = { - "winner_id": "1,2,4", - "assigned_id": [], - '报告标题': 'UI自动化测试报告', '项目名称': 'GitLink 确实开源', 'tester': '陈银花', - 'department': '开源中心', 'env': 'https://testforgeplus.trustie.net', - 'host': 'https://testforgeplus.trustie.net', 'login': 'autotest', - 'nickname': 'autotest', 'user_id': 106, 'super_login': 'floraachy', 'super_user_id': 103, - 'project_id': '59', - 'repo_id': '59', 'project_url': '/autotest/auotest', - 'attachment_ids': ['85b7f7ff-59e6-4f38-88da-29440aa4fc18', 'ba23f9b1-ad92-476d-ac4d-aba1382a9636'], - 'file_name': 'gitlinklogo3.jpg', - 'cookies': '{"_educoder_session": "d79e0e75f71cd98a9df2665d405b49e7", "autologin_trustie": "d25b412c26388182a50e8be38e4b9731c4e783ba"}', - } - - new = data_handle(obj=data, source=source) - print(new, type(new), - end="\n\n---------------------------------------------------------------------------------------------\n\n") - - print("\n-----------测试场景5:识别 字符串里面是python表达式的情况---------------------\n") - data = [ - "[1,2,3,4]", "1+1", "[1, '1', [1, 2], {'name':'flora', 'age': '1'}]" - ] - new = data_handle(data) - print(new, type(new), - end="\n\n---------------------------------------------------------------------------------------------\n\n") - - print("\n-----------测试场景5:导入的函数---------------------\n") - source = { - "added_testcase_test_step": [ - {'id': 5878, 'index': 0, 'content': '科技-大学', 'expectedResult': '一直-有些', 'execResult': 0}, - {'id': 5879, 'index': 1, 'content': '包括-质量', 'expectedResult': '系统-发表', 'execResult': 0}], - "test_ids": [1, 2, 3, 4, 5] - } - data = { - "testcaseStepList": "${data_keys_to_keep(${added_testcase_test_step},'id')}"} - - new = data_handle(obj=data, source=source) - print(new, type(new), - end="\n\n---------------------------------------------------------------------------------------------\n\n") - - data = { - "test_ids": '${list_to_str(target=${test_ids})}' - } - - new = data_handle(obj=data, source=source) - print(new, type(new), - end="\n\n---------------------------------------------------------------------------------------------\n\n") + # print(new, type(new), + # end="\n\n---------------------------------------------------------------------------------------------\n\n") + # + # print("-----------测试场景2:识别${python表达式},可以在当前文件导入其他模块,一样可以识别替换---------------------") + # # 导入其他方法,也可以直接使用 + # # from common_utils.time_handle import test_fun_a + # # data = "${test_fun_a()}" + # # new = data_handle(data) + # # print(new, type(new)) + # + # print("\n-----------测试场景3:识别FakerData类中的方法---------------------\n") + # """ + # 使用FakerData类中的方法可以直接这样写:${generate_random_int()}, 也可以带上类名:${FakerData().generate_random_int()} + # """ + # data = { + # "age": "${generate_random_int()}", + # "message": "Hello, ${FakerData().generate_female_name()}!", + # "nested_data": [ + # "This is ${name}'s data.", + # { + # "message": "Age: ${generate_random_int()}", + # "nested_list": [ + # "More data: ${FakerData().generate_random_int()}", + # ] + # } + # ] + # } + # new = data_handle(data) + # print(new, type(new), end="\n\n") + # + # """ + # 使用FakerData类中的方法, 支持方法传参使用,注意参数如果是str格式,建议使用单引号 + # """ + # payload = { + # "name": "${generate_name(lan='zh')}", + # "repository_name": "${generate_name('zh')}", + # "desc": '[[1,2,3,4],"${FakerData().generate_random_int()}"]', + # "pre": '[[1,2,3,4],${FakerData().generate_name()}]', + # "startTime": "${FakerData.generate_time('%Y-%m-%d')}", + # } + # new = data_handle(payload) + # print(new, type(new), end="\n\n") + # + # """ + # 还可以直接使用FakerData类中的实例属性 + # """ + # + # data = { + # "payload": { + # "en_name": "${faker.name()}", # 这里是使用类FakerData里面的实例属性faker + # "zh_name": "${fk_zh.name()}", # 这里是使用类FakerData里面的实例属性fk_zh + # "url": "/api/accounts/${FakerData.generate_time('%Y-%m-%d')}/login.json", + # } + # } + # + # new = data_handle(data) + # print(new, type(new), end="\n\n") + # + # """ + # FakerData类中没有封装random_name这个方法,会无法处理 + # """ + # data = '[[1,2,3,4],"${FakerData().random_name()}"]' + # new = data_handle(data) + # print(new, type(new), + # end="\n\n---------------------------------------------------------------------------------------------\n\n") + # + # print("\n-----------测试场景4:识别${}进行关键字替换---------------------\n") + # user_info = { + # "user_id": 104, + # "user_name": "flora" + # } + # data_03 = "user_id: ${user_id}, user_name: ${user_name}" + # new = data_handle(data_03, user_info) + # print(new, type(new), end="\n\n") + # + # """ + # 识别${}进行关键字替换时会保留原值的类型。 比如eval('1,2,4')会变成元组(1,2,4)。经过本方法处理,会保留原有格式 + # """ + # data = { + # "winner_id": "${winner_id}", + # "user_id": "${user_id}", + # "time": "${generate_time()}", + # "attachment_ids": "${attachment_ids}", + # "assigned_id": "${assigned_id}", + # "cookies": "${cookies}" + # } + # source = { + # "winner_id": "1,2,4", + # "assigned_id": [], + # '报告标题': 'UI自动化测试报告', '项目名称': 'GitLink 确实开源', 'tester': '陈银花', + # 'department': '开源中心', 'env': 'https://testforgeplus.trustie.net', + # 'host': 'https://testforgeplus.trustie.net', 'login': 'autotest', + # 'nickname': 'autotest', 'user_id': 106, 'super_login': 'floraachy', 'super_user_id': 103, + # 'project_id': '59', + # 'repo_id': '59', 'project_url': '/autotest/auotest', + # 'attachment_ids': ['85b7f7ff-59e6-4f38-88da-29440aa4fc18', 'ba23f9b1-ad92-476d-ac4d-aba1382a9636'], + # 'file_name': 'gitlinklogo3.jpg', + # 'cookies': '{"_educoder_session": "d79e0e75f71cd98a9df2665d405b49e7", "autologin_trustie": "d25b412c26388182a50e8be38e4b9731c4e783ba"}', + # } + # + # new = data_handle(obj=data, source=source) + # print(new, type(new), + # end="\n\n---------------------------------------------------------------------------------------------\n\n") + # + # print("\n-----------测试场景5:识别 字符串里面是python表达式的情况---------------------\n") + # data = [ + # "[1,2,3,4]", "1+1", "[1, '1', [1, 2], {'name':'flora', 'age': '1'}]" + # ] + # new = data_handle(data) + # print(new, type(new), + # end="\n\n---------------------------------------------------------------------------------------------\n\n") + # + # print("\n-----------测试场景5:导入的函数---------------------\n") + # source = { + # "added_testcase_test_step": [ + # {'id': 5878, 'index': 0, 'content': '科技-大学', 'expectedResult': '一直-有些', 'execResult': 0}, + # {'id': 5879, 'index': 1, 'content': '包括-质量', 'expectedResult': '系统-发表', 'execResult': 0}], + # "test_ids": [1, 2, 3, 4, 5] + # } + # data = { + # "testcaseStepList": "${data_keys_to_keep(${added_testcase_test_step},'id')}"} + # + # new = data_handle(obj=data, source=source) + # print(new, type(new), + # end="\n\n---------------------------------------------------------------------------------------------\n\n") + # + # data = { + # "test_ids": '${list_to_str(target=${test_ids})}' + # } + # + # new = data_handle(obj=data, source=source) + # print(new, type(new), + # end="\n\n---------------------------------------------------------------------------------------------\n\n") diff --git a/custom_utils/data_utils/data_tools.py b/custom_utils/data_utils/data_tools.py index a11b3ad..193be15 100644 --- a/custom_utils/data_utils/data_tools.py +++ b/custom_utils/data_utils/data_tools.py @@ -54,11 +54,10 @@ def get_file_content(file_name): def list_to_str(target): """ - 将列表中的元素转换为字符串,并用逗号分隔。 - - :param target: 要转换为字符串的列表。 - :return: 以逗号分隔的字符串。 - """ + 将列表中的元素转换为字符串,并用逗号分隔。 + :param target: 要转换为字符串的列表。 + :return: 以逗号分隔的字符串。 + """ if isinstance(target, list) and target: # 过滤掉列表中的None值 filtered_list = [str(item) for item in target if item is not None] diff --git a/custom_utils/data_utils/extract_data_handle.py b/custom_utils/data_utils/extract_data_handle.py index 85631d3..55fd006 100644 --- a/custom_utils/data_utils/extract_data_handle.py +++ b/custom_utils/data_utils/extract_data_handle.py @@ -66,7 +66,8 @@ def response_extract(response: Response, expr: str = '.'): """ 从response响应对象提取cookies之类 :param response : response对象 - :param expr: 提取表达式。部分参考:response.status_code, response.cookies, response.text, response.headers, response.is_redirect + :param expr: 提取表达式。 + 部分参考:response.status_code, response.cookies, response.text, response.headers, response.is_redirect :return result: 提取的结果,未提取到返回 None """ try: diff --git a/custom_utils/requests_utils/request_control.py b/custom_utils/requests_utils/request_control.py index 2a0209d..3592da2 100644 --- a/custom_utils/requests_utils/request_control.py +++ b/custom_utils/requests_utils/request_control.py @@ -19,7 +19,7 @@ from settings import FILES_DIR from custom_utils.requests_utils.base_request import BaseRequest from custom_utils.data_utils.data_handle import data_handle from custom_utils.data_utils.extract_data_handle import json_extractor, re_extract, response_extract -from common_utils.files_utils.files_handle import get_files,load_yaml_file +from common_utils.files_utils.files_handle import get_files, load_yaml_file from custom_utils.assertion_utils.assert_control import AssertHandle from custom_utils.report_utils.allure_handle import allure_step from common_utils.database_utils.mysql_handle import MysqlServer @@ -117,24 +117,34 @@ class RequestControl(BaseRequest): f"cookies参数要求是Dict or CookieJar object, 目前cookies类型是:{type(cookies)}, cookies值是:{cookies}") @staticmethod - def headers_handle(headers: dict, source: dict = None): + def headers_handle(headers: dict = None, source: dict = None) -> dict: """ headers里面传Cookie,要求Cookie类型是str + + Args: + headers: 请求头字典,默认为空字典 + source: 数据源 + + Returns: + dict: 处理后的请求头字典 """ - if headers: - # 从用例数据中获取header, 处理header - headers = data_handle(obj=headers, source=source) - # 如果请求头中有cookies,需要进行单独处理 - if headers.get("Cookie", None): - cookies = headers["Cookie"] - if isinstance(cookies, dict): - headers["Cookie"] = '; '.join([f"{key}={value}" for key, value in cookies.items()]) - if isinstance(cookies, http.cookiejar.CookieJar): - cookies_dict = utils.dict_from_cookiejar(cookies) - headers["Cookie"] = '; '.join([f"{key}={value}" for key, value in cookies_dict.items()]) - if isinstance(cookies, str): - headers["Cookie"] = cookies - return headers + if headers is None: + headers = {} + + # 从用例数据中获取header,处理header + headers = data_handle(obj=headers, source=source) + + # 处理Cookie字段 + if headers.get("Cookie"): + cookies = headers["Cookie"] + if isinstance(cookies, dict): + headers["Cookie"] = '; '.join([f"{key}={value}" for key, value in cookies.items()]) + elif isinstance(cookies, http.cookiejar.CookieJar): + cookies_dict = utils.dict_from_cookiejar(cookies) + headers["Cookie"] = '; '.join([f"{key}={value}" for key, value in cookies_dict.items()]) + # str类型不需要处理,保持原样 + + return headers @staticmethod def files_handle(files: str, source: dict = None):