调整响应数据提取,支持通过yaml用例数据传参提取指定格式的参数
支持3种类型的数据提取:1. 通过jsonpath从response.json()提取数据; 2. 通过正则表达式从response.text提取; 3. 直接从response提取cookies之类;
This commit is contained in:
@@ -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")
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
24
case_utils/requests_utils/handle_eval_data.py
Normal file
24
case_utils/requests_utils/handle_eval_data.py
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user