mirror of
https://github.com/httprunner/httprunner.git
synced 2026-07-06 23:11:21 +08:00
feat: log detailed request & response when failed
This commit is contained in:
@@ -4,7 +4,7 @@ import jmespath
|
|||||||
import requests
|
import requests
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from httprunner.v3.exceptions import ValidationFailure
|
from httprunner.v3.exceptions import ValidationFailure, ParamsError
|
||||||
from httprunner.v3.parser import parse_data, parse_string_value, get_mapping_function
|
from httprunner.v3.parser import parse_data, parse_string_value, get_mapping_function
|
||||||
from httprunner.v3.schema import VariablesMapping, Validators, FunctionsMapping
|
from httprunner.v3.schema import VariablesMapping, Validators, FunctionsMapping
|
||||||
from httprunner.v3.validator import uniform_validator
|
from httprunner.v3.validator import uniform_validator
|
||||||
@@ -19,6 +19,7 @@ class ResponseObject(object):
|
|||||||
resp_obj (instance): requests.Response instance
|
resp_obj (instance): requests.Response instance
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
self.resp_obj = resp_obj
|
||||||
self.resp_obj_meta = {
|
self.resp_obj_meta = {
|
||||||
"status_code": resp_obj.status_code,
|
"status_code": resp_obj.status_code,
|
||||||
"headers": resp_obj.headers,
|
"headers": resp_obj.headers,
|
||||||
@@ -26,6 +27,22 @@ class ResponseObject(object):
|
|||||||
}
|
}
|
||||||
self.validation_results = {}
|
self.validation_results = {}
|
||||||
|
|
||||||
|
def __getattr__(self, key):
|
||||||
|
try:
|
||||||
|
if key == "json":
|
||||||
|
value = self.resp_obj.json()
|
||||||
|
elif key == "cookies":
|
||||||
|
value = self.resp_obj.cookies.get_dict()
|
||||||
|
else:
|
||||||
|
value = getattr(self.resp_obj, key)
|
||||||
|
|
||||||
|
self.__dict__[key] = value
|
||||||
|
return value
|
||||||
|
except AttributeError:
|
||||||
|
err_msg = f"ResponseObject does not have attribute: {key}"
|
||||||
|
logger.error(err_msg)
|
||||||
|
raise ParamsError(err_msg)
|
||||||
|
|
||||||
def extract(self, extractors: Dict[Text, Text]) -> Dict[Text, Any]:
|
def extract(self, extractors: Dict[Text, Text]) -> Dict[Text, Any]:
|
||||||
if not extractors:
|
if not extractors:
|
||||||
return {}
|
return {}
|
||||||
@@ -90,13 +107,11 @@ class ResponseObject(object):
|
|||||||
validate_pass = False
|
validate_pass = False
|
||||||
validator_dict["check_result"] = "fail"
|
validator_dict["check_result"] = "fail"
|
||||||
validate_msg += "\t==> fail"
|
validate_msg += "\t==> fail"
|
||||||
validate_msg += "\n{}({}) {} {}({})".format(
|
validate_msg += f"\n" \
|
||||||
check_value,
|
f"check_item: {check_item}\n" \
|
||||||
type(check_value).__name__,
|
f"check_value: {check_value}({type(check_value).__name__})\n" \
|
||||||
assert_method,
|
f"assert_method: {assert_method}\n" \
|
||||||
expect_value,
|
f"expect_value: {expect_value}({type(expect_value).__name__})"
|
||||||
type(expect_value).__name__
|
|
||||||
)
|
|
||||||
logger.error(validate_msg)
|
logger.error(validate_msg)
|
||||||
failures.append(validate_msg)
|
failures.append(validate_msg)
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from typing import List
|
|||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
|
from httprunner import utils
|
||||||
from httprunner.client import HttpSession
|
from httprunner.client import HttpSession
|
||||||
from httprunner.v3.exceptions import ValidationFailure
|
from httprunner.v3.exceptions import ValidationFailure
|
||||||
from httprunner.v3.parser import build_url, parse_data, parse_variables_mapping
|
from httprunner.v3.parser import build_url, parse_data, parse_variables_mapping
|
||||||
@@ -52,6 +53,28 @@ class TestCaseRunner(object):
|
|||||||
resp = self.session.request(method, url, **parsed_request_dict)
|
resp = self.session.request(method, url, **parsed_request_dict)
|
||||||
resp_obj = ResponseObject(resp)
|
resp_obj = ResponseObject(resp)
|
||||||
|
|
||||||
|
def log_req_resp_details():
|
||||||
|
err_msg = "\n{} DETAILED REQUEST & RESPONSE {}\n".format("*" * 32, "*" * 32)
|
||||||
|
|
||||||
|
# log request
|
||||||
|
err_msg += "====== request details ======\n"
|
||||||
|
err_msg += f"url: {url}\n"
|
||||||
|
err_msg += f"method: {method}\n"
|
||||||
|
headers = parsed_request_dict.pop("headers", {})
|
||||||
|
err_msg += f"headers: {headers}\n"
|
||||||
|
for k, v in parsed_request_dict.items():
|
||||||
|
v = utils.omit_long_data(v)
|
||||||
|
err_msg += f"{k}: {repr(v)}\n"
|
||||||
|
|
||||||
|
err_msg += "\n"
|
||||||
|
|
||||||
|
# log response
|
||||||
|
err_msg += "====== response details ======\n"
|
||||||
|
err_msg += f"status_code: {resp_obj.status_code}\n"
|
||||||
|
err_msg += f"headers: {resp_obj.headers}\n"
|
||||||
|
err_msg += f"body: {repr(resp_obj.text)}\n"
|
||||||
|
logger.error(err_msg)
|
||||||
|
|
||||||
# extract
|
# extract
|
||||||
extractors = step.extract
|
extractors = step.extract
|
||||||
extract_mapping = resp_obj.extract(extractors)
|
extract_mapping = resp_obj.extract(extractors)
|
||||||
@@ -64,6 +87,7 @@ class TestCaseRunner(object):
|
|||||||
try:
|
try:
|
||||||
resp_obj.validate(validators, variables_mapping, self.config.functions)
|
resp_obj.validate(validators, variables_mapping, self.config.functions)
|
||||||
except ValidationFailure:
|
except ValidationFailure:
|
||||||
|
log_req_resp_details()
|
||||||
raise
|
raise
|
||||||
finally:
|
finally:
|
||||||
self.validation_results = resp_obj.validation_results
|
self.validation_results = resp_obj.validation_results
|
||||||
|
|||||||
Reference in New Issue
Block a user