diff --git a/case_utils/requests_utils/data_handle.py b/case_utils/requests_utils/data_handle.py index 5d5046e..804841d 100644 --- a/case_utils/requests_utils/data_handle.py +++ b/case_utils/requests_utils/data_handle.py @@ -305,11 +305,9 @@ class DataHandle: """ func = {} keys = {} - if not source or not isinstance(source, dict): - # logger.debug("source为空或者source不是字典格式,都将认为是:{}") - source = {} - else: - logger.debug(f"source={source}") + + source = {} if not source or not isinstance(source, dict) else source + logger.trace(f"source={source}") # 处理一下source,检测到里面存在RequestsCookieJar,转成dict,再转换成JSON 格式的字符串(序列化)。 # 避免传递过来一个RequestsCookieJar,替换后变成了'RequestsCookieJar',导致cookies无法使用的问题 @@ -429,7 +427,7 @@ if __name__ == '__main__': "nested_data": [ "This is ${name}'s data.", { - "message": "Age: ${generate_random_int()", + "message": "Age: ${generate_random_int()}", "nested_list": [ "More data: ${FakerData().generate_random_int()}", ] @@ -520,4 +518,3 @@ if __name__ == '__main__': new = data_handle(data) print(new, type(new), end="\n\n---------------------------------------------------------------------------------------------\n\n") - diff --git a/case_utils/requests_utils/extract_data_handle.py b/case_utils/requests_utils/extract_data_handle.py index b452490..249eba4 100644 --- a/case_utils/requests_utils/extract_data_handle.py +++ b/case_utils/requests_utils/extract_data_handle.py @@ -10,52 +10,62 @@ import re # 第三方库导入 from jsonpath import jsonpath from loguru import logger +from requests import Response def json_extractor(obj: dict, expr: str = '.'): """ + 从目标对象obj, 根据表达式expr提取指定的值 :param obj :json/dict类型数据 :param expr: 表达式, . 提取字典所有内容, $.test_api_case 提取一级字典case, $.test_api_case.data 提取case字典下的data :return result: 提取的结果,未提取到返回 None """ try: - # 如果提取后的数据长度为1,则取第一个元素(返回str),否则返回列表 result = jsonpath(obj, expr)[0] if len(jsonpath(obj, expr)) == 1 else jsonpath(obj, expr) - logger.trace("\n======================================================\n" \ - "-------------Start:json_extractor--------------------\n" - f"提取表达式为: {expr} \n" - f"提取值为: {result}\n" - "=====================================================") + logger.trace(f"\n提取表达式: {expr} \n" + f"提取值类型: {type(result)}\n" + f"提取值:{result}\n") + return result except Exception as e: - logger.trace("\n======================================================\n" \ - "-------------End:json_extractor--------------------\n" - f"提取表达式为: {expr}\n" - f"提取数据为: {obj}\n" - f"错误信息为:{e}\n" - "=====================================================") - result = None - return result + logger.trace(f"\n提取表达式: {expr}\n" + f"提取对象: {obj}\n" + f"错误信息:{e}\n") def re_extract(obj: str, expr: str = '.'): """ + 从目标对象obj, 根据表达式expr提取指定的值 :param obj : 字符串数据 :param expr: 正则表达式 :return result: 提取的结果,未提取到返回 None """ try: - result = re.findall(expr, obj)[0] - logger.trace("\n======================================================\n" \ - "-------------Start:re_extract--------------------\n" - f"提取表达式为: {expr}\n" \ - f"提取值为: {result}\n" \ - "=====================================================") + # 如果提取后的数据长度为1,则取第一个元素(返回str),否则返回列表 + result = re.findall(expr, obj)[0] if len(re.findall(expr, obj)) == 1 else re.findall(expr, obj) + logger.debug(f"\n提取表达式: {expr}\n" + f"提取值类型: {type(result)}\n" + f"提取值:{result}\n") + return result except Exception as e: - logger.trace(f"\n======================================================\n" \ - "-------------End:re_extract--------------------\n" - f"提取表达式为: {expr}\n" \ - f"提取数据为: {obj}\n" \ - f"错误信息为:{e}\n" \ - "=====================================================") - result = None - return result + logger.debug(f"\n提取表达式: {expr}\n" + f"提取对象: {obj}\n" + f"错误信息:{e}\n") + + +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 + :return result: 提取的结果,未提取到返回 None + """ + try: + result = eval(expr) + logger.debug(f"\n提取表达式: {expr}\n" + f"提取值类型: {type(result)}\n" + f"提取值:{result}\n") + return result + except Exception as e: + logger.debug(f"\n提取表达式: {expr}\n" + f"提取对象: {response}\n" + f"错误信息:{e}\n") diff --git a/case_utils/requests_utils/handle_eval_data.py b/case_utils/requests_utils/handle_eval_data.py new file mode 100644 index 0000000..6002cdd --- /dev/null +++ b/case_utils/requests_utils/handle_eval_data.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# @Time : 2023/12/5 10:19 +# @Author : floraachy +# @File : handle_eval_data +# @Software: PyCharm +# @Desc: + +# 第三方库导入 +from loguru import logger + + +# 将"[1,2,3]" 或者"{'k':'v'}" -> [1,2,3], {'k':'v'} +def eval_data(data): + """ + 执行一个字符串表达式,并返回其表达式的值 + """ + try: + if hasattr(eval(data), "__call__"): + return data + else: + return eval(data) + except Exception as e: + logger.trace(f"{data} --> 该数据不能被eval\n报错:{e}") + return data diff --git a/case_utils/requests_utils/request_data_handle.py b/case_utils/requests_utils/request_data_handle.py index 3b12b97..c664ff8 100644 --- a/case_utils/requests_utils/request_data_handle.py +++ b/case_utils/requests_utils/request_data_handle.py @@ -14,13 +14,13 @@ import http.cookiejar from requests import Response from loguru import logger # 本地应用/模块导入 -from common_utils.other_utils.files_handle import get_file_field from case_utils.requests_utils.base_request import BaseRequest from case_utils.requests_utils.data_handle import data_handle -from case_utils.requests_utils.extract_data_handle import json_extractor, re_extract +from case_utils.requests_utils.extract_data_handle import json_extractor, re_extract, response_extract from case_utils.report_utils.allure_handle import allure_step from config.global_vars import GLOBAL_VARS from config.path_config import FILES_DIR +from case_utils.requests_utils.handle_eval_data import eval_data # ---------------------------------------- 请求前的数据处理----------------------------------------# @@ -210,35 +210,47 @@ class RequestHandle: # ---------------------------------------- 请求后的参数提取处理----------------------------------------# def after_request_extract(response: Response, extract): """ - 从响应数据中提取请求后的参数,并保存到全局变量中 - :param response: request 响应对象 - :param extract: 需要提取的参数字典 '{"k1": "$.data"}' 或 '{"k1": "data:(.*?)$"}' - :return: - """ - logger.debug(f"\n======================================================\n" \ - "-------------Start:从响应数据中提取后置参数保存到全局变量--------------------\n" - f"后置提取参数(原): {extract}\n" \ - "=====================================================") - result = {} + 从响应数据中提取请求后的参数,并保存到全局变量中 + :param response: request 响应对象 + :param extract: 需要提取的参数字典 '{"k1": "$.data"}' 或 '{"k1": "data:(.*?)$"}' + :return: + """ + logger.debug(f"\n================================================================================\n" \ + "-------------Start: 提取表达式--------------------\n" + f"后置提取参数(原): {extract}\n") + json_result = {} + re_result = {} + response_result = {} if extract: - if response_type(response) == "json": + if extract.get("type_json"): # 如果响应数据是json格式,则将按照json方式对后置提取参数进行处理 res = response.json() - for k, v in extract.items(): - result[k] = json_extractor(res, v) - else: + for k, v in extract["type_json"].items(): + json_result[k] = json_extractor(res, v) + logger.debug("-------------从response.json()中通过jsonpath方式提取到的结果--------------------\n" + f"后置提取参数(新): {json_result}\n") + if extract.get("type_re"): # 如果响应数据是str格式,则将按照str方式对后置提取参数进行处理 res = response.text - for k, v in extract.items(): - result[k] = re_extract(res, v) - logger.debug(f"\n======================================================\n" \ - "-------------End:从响应数据中提取后置参数保存到全局变量--------------------\n" - f"后置提取参数(新): {result}\n" \ - "=====================================================") + for k, v in extract["type_re"].items(): + re_result[k] = re_extract(res, v) + logger.debug("-------------从response.text中通过正则表达式提取到的结果--------------------\n" + f"后置提取参数(新): {re_result}\n") + + if extract.get("type_response"): + for k, v in extract["type_response"].items(): + response_result[k] = response_extract(response, v) + logger.debug("-------------从response中提取到的结果--------------------\n" + f"后置提取参数(新): {re_result}\n") + result = {**json_result, **re_result, **response_result} # 将提取到的变量保存在全局变量中 if result: for k, v in result.items(): - GLOBAL_VARS[k] = v + GLOBAL_VARS[k] = eval_data(v) + result[k] = eval_data(v) + logger.info("-------------End:所有提取到的结果--------------------\n" + f"后置提取参数(新): {result}\n" \ + "================================================================================") return result diff --git a/config/settings.py b/config/settings.py index 4e1cc23..8ab0376 100644 --- a/config/settings.py +++ b/config/settings.py @@ -90,7 +90,7 @@ class RunConfig: CASE_FILE_TYPE = 1 # 0表示默认不发送任何通知, 1代表钉钉通知,2代表企业微信通知, 3代表邮件通知, 4代表所有途径都发送通知 -SEND_RESULT_TYPE = 1 +SEND_RESULT_TYPE = 0 # 指定日志收集级别 LOG_LEVEL = "DEBUG" diff --git a/data/gitlink/glcc/test_get_apply_information.yml b/data/gitlink/glcc/test_get_apply_information.yml index 60fb752..4650230 100644 --- a/data/gitlink/glcc/test_get_apply_information.yml +++ b/data/gitlink/glcc/test_get_apply_information.yml @@ -13,12 +13,15 @@ case_info: title: 获取已报名成功的项目数据 severity: normal run: True - url: ${glcc_host}/api/applyInformation/list?curPage=1&pageSize=10000&round=2 + url: ${glcc_host}/api/applyInformation/list method: GET headers: {"Content-Type": "application/json; charset=utf-8;"} cookies: - request_type: json + request_type: params payload: + curPage: 1 + pageSize: 10000 + round: 2 files: extract: assert_response: @@ -32,12 +35,15 @@ case_info: title: 获取已报名成功的课题数据 severity: normal run: True - url: https://glcc.gitlink.org.cn/api/applyInformation/taskList?curPage=1&pageSize=20&userId=&round=2 + url: ${glcc_host}/api/applyInformation/taskList method: GET headers: {"Content-Type": "application/json; charset=utf-8;"} cookies: - request_type: json + request_type: params payload: + curPage: 1 + pageSize: 20 + round: 2 files: extract: assert_response: diff --git a/data/gitlink/test_login.yaml b/data/gitlink/test_login.yaml index 0b4f60c..58a6584 100644 --- a/data/gitlink/test_login.yaml +++ b/data/gitlink/test_login.yaml @@ -22,9 +22,10 @@ case_info: payload: { "login": "${login}","password": "${password}","autologin": 1 } files: extract: - nickname: $.username - login: $.login - user_id: $.user_id + type_re: + nickname: \"username":"(.*?)" + login: \"login":"(.*?)" + user_id: \"user_id":(.*?), assert_response: eq: http_code: 200 diff --git a/data/gitlink/test_new_project_demo.yaml b/data/gitlink/test_new_project_demo.yaml index 23e212c..927fe9a 100644 --- a/data/gitlink/test_new_project_demo.yaml +++ b/data/gitlink/test_new_project_demo.yaml @@ -23,14 +23,15 @@ case_info: cookies: request_type: json payload: - "user_id": ${user_id} - "name": ${generate_name(lan='zh')}_${generate_identifier()} - "repository_name": ${generate_identifier()} + user_id: ${user_id} + name: ${generate_name(lan='zh')}_${generate_identifier()} + repository_name: ${generate_identifier()} files: extract: - project_id: $.id - project_name: $.name - project_identifier: $.identifier + type_json: + project_id: $.id + project_name: $.name + project_identifier: $.identifier assert_response: eq: http_code: 200 @@ -48,9 +49,9 @@ case_info: cookies: ${login_cookie} request_type: json payload: - "user_id": ${user_id} - "name": ${generate_name(lan='zh')}_${generate_identifier()} - "repository_name": ${generate_identifier()} + user_id: ${user_id} + name: ${generate_name(lan='zh')}_${generate_identifier()} + repository_name: ${generate_identifier()} files: extract: project_id: $.id @@ -73,9 +74,9 @@ case_info: cookies: request_type: json payload: - "user_id": ${user_id} - "name": ${generate_name(lan='zh')}_${generate_identifier()} - "repository_name": ${generate_identifier()} + user_id: ${user_id} + name: ${generate_name(lan='zh')}_${generate_identifier()} + repository_name: ${generate_identifier()} files: extract: project_id: $.id diff --git a/data/gitlink/test_upload_files.yaml b/data/gitlink/test_upload_files.yaml index 522204e..4cf25a4 100644 --- a/data/gitlink/test_upload_files.yaml +++ b/data/gitlink/test_upload_files.yaml @@ -24,7 +24,8 @@ case_info: payload: files: TOC出库订单导入模板(2).xlsx extract: - file_id: $.id + type_json: + file_id: $.id assert_response: eq: http_code: 200