diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 0027879b..195a7a40 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -12,6 +12,7 @@ **python version** - feat: support skip for pytest +- feat: print request and response details when running API cases - fix: support None/dict/list format when printing sql response ## v4.1.3 (2022-06-14) diff --git a/httprunner/runner.py b/httprunner/runner.py index ecc0c0b7..3b73778c 100644 --- a/httprunner/runner.py +++ b/httprunner/runner.py @@ -227,12 +227,16 @@ class SessionRunner(object): finally: logger.info(f"generate testcase log: {self.__log_path}") if USE_ALLURE: - allure.attach.file(self.__log_path, name='all log', attachment_type=allure.attachment_type.TEXT) + allure.attach.file( + self.__log_path, + name="all log", + attachment_type=allure.attachment_type.TEXT, + ) self.__duration = time.time() - self.__start_at return self class HttpRunner(SessionRunner): - # split SessionRunner to keep consistant with golang version + # split SessionRunner to keep consistent with golang version pass diff --git a/httprunner/step_request.py b/httprunner/step_request.py index f08bc636..8050ab2e 100644 --- a/httprunner/step_request.py +++ b/httprunner/step_request.py @@ -1,7 +1,8 @@ -import copy +import json import time from typing import Any, Dict, List, Text, Union +import requests from loguru import logger from httprunner import utils @@ -18,7 +19,7 @@ from httprunner.models import ( ) from httprunner.parser import build_url from httprunner.response import ResponseObject -from httprunner.runner import HttpRunner, USE_ALLURE +from httprunner.runner import USE_ALLURE, HttpRunner def call_hooks( @@ -66,6 +67,16 @@ def call_hooks( logger.error(f"Invalid hook format: {hook}") +def pretty_format(v) -> str: + if isinstance(v, dict): + return json.dumps(v, indent=4, ensure_ascii=False) + + if isinstance(v, requests.structures.CaseInsensitiveDict): + return json.dumps(dict(v.items()), indent=4, ensure_ascii=False) + + return repr(utils.omit_long_data(v)) + + def run_step_request(runner: HttpRunner, step: TStep) -> StepResult: """run teststep: request""" step_result = StepResult( @@ -104,28 +115,40 @@ def run_step_request(runner: HttpRunner, step: TStep) -> StepResult: request_print = "====== request details ======\n" request_print += f"url: {url}\n" request_print += f"method: {method}\n" - headers = parsed_request_dict.get("headers", {}) - request_print += f"headers: {headers}\n" for k, v in parsed_request_dict.items(): - v = utils.omit_long_data(v) - request_print += f"{k}: {repr(v)}\n" - request_print += "\n" + request_print += f"{k}: {pretty_format(v)}\n" - # request + print(request_print) if USE_ALLURE: import allure - allure.attach(request_print, name="request details", attachment_type=allure.attachment_type.TEXT) + + allure.attach( + request_print, + name="request details", + attachment_type=allure.attachment_type.TEXT, + ) resp = runner.session.request(method, url, **parsed_request_dict) # log response response_print = "====== response details ======\n" response_print += f"status_code: {resp.status_code}\n" - response_print += f"headers: {resp.headers}\n" - response_print += f"body: {repr(resp.text)}\n" + response_print += f"headers: {pretty_format(resp.headers)}\n" + try: + resp_body = resp.json() + except requests.exceptions.JSONDecodeError: + resp_body = resp.content + + response_print += f"body: {pretty_format(resp_body)}\n" + print(response_print) if USE_ALLURE: import allure - allure.attach(response_print, name="response details", attachment_type=allure.attachment_type.TEXT) + + allure.attach( + response_print, + name="response details", + attachment_type=allure.attachment_type.TEXT, + ) resp_obj = ResponseObject(resp, runner.parser) step_variables["response"] = resp_obj @@ -133,11 +156,6 @@ def run_step_request(runner: HttpRunner, step: TStep) -> StepResult: if step.teardown_hooks: call_hooks(runner, step.teardown_hooks, step_variables, "teardown request") - def log_req_resp_details(): - err_msg = "\n{} DETAILED REQUEST & RESPONSE {}\n".format("*" * 32, "*" * 32) - err_msg += request_print + response_print - logger.error(err_msg) - # extract extractors = step.extract extract_mapping = resp_obj.extract(extractors, step_variables) @@ -152,7 +170,6 @@ def run_step_request(runner: HttpRunner, step: TStep) -> StepResult: resp_obj.validate(validators, variables_mapping) step_result.success = True except ValidationFailure: - log_req_resp_details() raise finally: session_data = runner.session.data