mirror of
https://github.com/httprunner/httprunner.git
synced 2026-09-05 23:51:25 +08:00
record more info about request and response, and display in report
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
__title__ = 'HttpRunner'
|
__title__ = 'HttpRunner'
|
||||||
__description__ = 'One-stop solution for HTTP(S) testing.'
|
__description__ = 'One-stop solution for HTTP(S) testing.'
|
||||||
__url__ = 'https://github.com/HttpRunner/HttpRunner'
|
__url__ = 'https://github.com/HttpRunner/HttpRunner'
|
||||||
__version__ = '1.5.3'
|
__version__ = '1.5.4'
|
||||||
__author__ = 'debugtalk'
|
__author__ = 'debugtalk'
|
||||||
__author_email__ = 'mail@debugtalk.com'
|
__author_email__ = 'mail@debugtalk.com'
|
||||||
__license__ = 'MIT'
|
__license__ = 'MIT'
|
||||||
|
|||||||
+54
-36
@@ -55,17 +55,20 @@ class HttpSession(requests.Session):
|
|||||||
""" initialize meta_data, it will store detail data of request and response
|
""" initialize meta_data, it will store detail data of request and response
|
||||||
"""
|
"""
|
||||||
self.meta_data = {
|
self.meta_data = {
|
||||||
"url": "N/A",
|
"request": {
|
||||||
"method": "N/A",
|
"url": "N/A",
|
||||||
"request_time": "N/A",
|
"method": "N/A",
|
||||||
"request_headers": {},
|
"headers": {},
|
||||||
"request_body": "N/A",
|
"start_timestamp": None
|
||||||
"status_code": "N/A",
|
},
|
||||||
"response_headers": {},
|
"response": {
|
||||||
"response_body": "N/A",
|
"status_code": "N/A",
|
||||||
"content_size": "N/A",
|
"headers": {},
|
||||||
"response_time_ms": "N/A",
|
"content_size": "N/A",
|
||||||
"elapsed_ms": "N/A"
|
"response_time_ms": "N/A",
|
||||||
|
"elapsed_ms": "N/A",
|
||||||
|
"content": None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def request(self, method, url, name=None, **kwargs):
|
def request(self, method, url, name=None, **kwargs):
|
||||||
@@ -107,10 +110,17 @@ class HttpSession(requests.Session):
|
|||||||
:param cert: (optional)
|
:param cert: (optional)
|
||||||
if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair.
|
if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair.
|
||||||
"""
|
"""
|
||||||
|
def log_print(request_response):
|
||||||
|
msg = "\n================== {} details ==================\n".format(request_response)
|
||||||
|
for key, value in self.meta_data[request_response].items():
|
||||||
|
msg += "{:<16} : {}\n".format(key, value)
|
||||||
|
logger.log_debug(msg)
|
||||||
|
|
||||||
# record original request info
|
# record original request info
|
||||||
self.meta_data["method"] = method
|
self.meta_data["request"]["method"] = method
|
||||||
self.meta_data["url"] = url
|
self.meta_data["request"]["url"] = url
|
||||||
self.meta_data["request_time"] = time.time()
|
self.meta_data["request"].update(kwargs)
|
||||||
|
self.meta_data["request"]["start_timestamp"] = time.time()
|
||||||
|
|
||||||
# prepend url with hostname unless it's already an absolute URL
|
# prepend url with hostname unless it's already an absolute URL
|
||||||
url = self._build_url(url)
|
url = self._build_url(url)
|
||||||
@@ -119,35 +129,43 @@ class HttpSession(requests.Session):
|
|||||||
response = self._send_request_safe_mode(method, url, **kwargs)
|
response = self._send_request_safe_mode(method, url, **kwargs)
|
||||||
|
|
||||||
# record the consumed time
|
# record the consumed time
|
||||||
self.meta_data["response_time_ms"] = round((time.time() - self.meta_data["request_time"]) * 1000, 2)
|
self.meta_data["response"]["response_time_ms"] = \
|
||||||
self.meta_data["elapsed_ms"] = response.elapsed.microseconds / 1000.0
|
round((time.time() - self.meta_data["request"]["start_timestamp"]) * 1000, 2)
|
||||||
|
self.meta_data["response"]["elapsed_ms"] = response.elapsed.microseconds / 1000.0
|
||||||
|
|
||||||
# record actual request info
|
# record actual request info
|
||||||
self.meta_data["url"] = (response.history and response.history[0] or response).request.url
|
self.meta_data["request"]["url"] = (response.history and response.history[0] or response).request.url
|
||||||
self.meta_data["request_headers"] = response.request.headers
|
self.meta_data["request"]["headers"] = response.request.headers
|
||||||
self.meta_data["request_body"] = response.request.body
|
self.meta_data["request"]["body"] = response.request.body
|
||||||
|
|
||||||
|
# log request details in debug mode
|
||||||
|
log_print("request")
|
||||||
|
|
||||||
# record response info
|
# record response info
|
||||||
self.meta_data["status_code"] = response.status_code
|
self.meta_data["response"]["ok"] = response.ok
|
||||||
self.meta_data["response_headers"] = response.headers
|
self.meta_data["response"]["url"] = response.url
|
||||||
try:
|
self.meta_data["response"]["status_code"] = response.status_code
|
||||||
self.meta_data["response_body"] = response.json()
|
self.meta_data["response"]["reason"] = response.reason
|
||||||
except ValueError:
|
self.meta_data["response"]["headers"] = response.headers
|
||||||
self.meta_data["response_body"] = response.content
|
self.meta_data["response"]["cookies"] = response.cookies or {}
|
||||||
|
self.meta_data["response"]["encoding"] = response.encoding
|
||||||
|
self.meta_data["response"]["content"] = response.content
|
||||||
|
self.meta_data["response"]["text"] = response.text
|
||||||
|
|
||||||
# log response details in debug mode
|
try:
|
||||||
msg = "response details:\n"
|
self.meta_data["response"]["json"] = response.json()
|
||||||
msg += "> status_code: {}\n".format(self.meta_data["status_code"])
|
except ValueError:
|
||||||
msg += "> headers: {}\n".format(self.meta_data["response_headers"])
|
self.meta_data["response"]["json"] = None
|
||||||
msg += "> body: {}".format(self.meta_data["response_body"])
|
|
||||||
logger.log_debug(msg)
|
|
||||||
|
|
||||||
# get the length of the content, but if the argument stream is set to True, we take
|
# get the length of the content, but if the argument stream is set to True, we take
|
||||||
# the size from the content-length header, in order to not trigger fetching of the body
|
# the size from the content-length header, in order to not trigger fetching of the body
|
||||||
if kwargs.get("stream", False):
|
if kwargs.get("stream", False):
|
||||||
self.meta_data["content_size"] = int(self.meta_data["response_headers"].get("content-length") or 0)
|
self.meta_data["response"]["content_size"] = int(self.meta_data["response"]["headers"].get("content-length") or 0)
|
||||||
else:
|
else:
|
||||||
self.meta_data["content_size"] = len(response.content or "")
|
self.meta_data["response"]["content_size"] = len(response.content or "")
|
||||||
|
|
||||||
|
# log response details in debug mode
|
||||||
|
log_print("response")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
@@ -156,9 +174,9 @@ class HttpSession(requests.Session):
|
|||||||
else:
|
else:
|
||||||
logger.log_info(
|
logger.log_info(
|
||||||
"""status_code: {}, response_time(ms): {} ms, response_length: {} bytes""".format(
|
"""status_code: {}, response_time(ms): {} ms, response_length: {} bytes""".format(
|
||||||
self.meta_data["status_code"],
|
self.meta_data["response"]["status_code"],
|
||||||
self.meta_data["response_time_ms"],
|
self.meta_data["response"]["response_time_ms"],
|
||||||
self.meta_data["content_size"]
|
self.meta_data["response"]["content_size"]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ def stringify_body(meta_data, request_or_response):
|
|||||||
resp_content_type = headers.get("Content-Type", "")
|
resp_content_type = headers.get("Content-Type", "")
|
||||||
try:
|
try:
|
||||||
if "image" in resp_content_type:
|
if "image" in resp_content_type:
|
||||||
meta_data["response_data_type"] = "image"
|
meta_data["response"]["data_type"] = "image"
|
||||||
body = "data:{};base64,{}".format(
|
body = "data:{};base64,{}".format(
|
||||||
resp_content_type,
|
resp_content_type,
|
||||||
b64encode(body).decode('utf-8')
|
b64encode(body).decode('utf-8')
|
||||||
|
|||||||
@@ -236,7 +236,7 @@
|
|||||||
<tr id="record_{{record_index}}">
|
<tr id="record_{{record_index}}">
|
||||||
<th class="{{record.status}}" style="width:5em;">{{record.status}}</td>
|
<th class="{{record.status}}" style="width:5em;">{{record.status}}</td>
|
||||||
<td colspan="2">{{record.name}}</td>
|
<td colspan="2">{{record.name}}</td>
|
||||||
<td style="text-align:center;width:6em;">{{ record.meta_data.response_time_ms }} ms</td>
|
<td style="text-align:center;width:6em;">{{ record.meta_data.response.response_time_ms }} ms</td>
|
||||||
<td class="detail">
|
<td class="detail">
|
||||||
|
|
||||||
<a class="button" href="#popup_log_{{record_index}}">log</a>
|
<a class="button" href="#popup_log_{{record_index}}">log</a>
|
||||||
@@ -249,66 +249,51 @@
|
|||||||
<h3>Request:</h3>
|
<h3>Request:</h3>
|
||||||
<div style="overflow: auto">
|
<div style="overflow: auto">
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
{% for key, value in record.meta_data.request.items() %}
|
||||||
<th>url</th>
|
<tr>
|
||||||
<td>{{ record.meta_data.url }}</td>
|
<th>{{key}}</th>
|
||||||
</tr>
|
<td>
|
||||||
<tr>
|
{% if key == "headers" %}
|
||||||
<th>method</th>
|
{% for header_key, header_value in record.meta_data.request.headers.items() %}
|
||||||
<td>{{ record.meta_data.method }}</td>
|
<div>
|
||||||
</tr>
|
<strong>{{ header_key }}</strong>: {{ header_value }}
|
||||||
<tr>
|
</div>
|
||||||
<th>headers</th>
|
{% endfor %}
|
||||||
<td>
|
{% else %}
|
||||||
{% for key, value in record.meta_data.request_headers.items() %}
|
{{value}}
|
||||||
<div>
|
{% endif %}
|
||||||
<strong>{{ key }}</strong>: {{ value }}
|
</td>
|
||||||
</div>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% if record.meta_data.method in ["POST", "PUT"] %}
|
|
||||||
<tr>
|
|
||||||
<th>body</th>
|
|
||||||
<td>
|
|
||||||
<pre>{{ record.meta_data.request_body }}</pre>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3>Response:</h3>
|
<h3>Response:</h3>
|
||||||
<div style="overflow: auto">
|
<div style="overflow: auto">
|
||||||
<table>
|
<table>
|
||||||
<tr>
|
{% for key, value in record.meta_data.response.items() %}
|
||||||
<th>status_code</th>
|
<tr>
|
||||||
<td>
|
<th>{{key}}</th>
|
||||||
{{ record.meta_data.status_code }}
|
<td>
|
||||||
</td>
|
{% if key == "headers" %}
|
||||||
</tr>
|
{% for header_key, header_value in record.meta_data.request.headers.items() %}
|
||||||
<tr>
|
<div>
|
||||||
<th>headers</th>
|
<strong>{{ header_key }}</strong>: {{ header_value }}
|
||||||
<td>
|
</div>
|
||||||
{% for key, value in record.meta_data.response_headers.items() %}
|
{% endfor %}
|
||||||
<div>
|
{% elif key == "data_type" %}
|
||||||
<strong>{{ key }}</strong>: {{ value }}
|
{% if value == "image" %}
|
||||||
</div>
|
<img src="{{ record.meta_data.response.body }}" />
|
||||||
{% endfor %}
|
{% else %}
|
||||||
</td>
|
<pre>{{ record.meta_data.response.body }}</pre>
|
||||||
</tr>
|
{% endif %}
|
||||||
<tr>
|
{% else %}
|
||||||
<th>body</th>
|
{{value}}
|
||||||
<td>
|
{% endif %}
|
||||||
{% if record.meta_data.response_data_type == "image" %}
|
</td>
|
||||||
<img src="{{ record.meta_data.response_body }}" />
|
</tr>
|
||||||
{% else %}
|
{% endfor %}
|
||||||
<pre>{{ record.meta_data.response_body }}</pre>
|
</table>
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3>Validators:</h3>
|
<h3>Validators:</h3>
|
||||||
@@ -344,15 +329,15 @@
|
|||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<th>content_size(bytes)</th>
|
<th>content_size(bytes)</th>
|
||||||
<td>{{ record.meta_data.content_size }}</td>
|
<td>{{ record.meta_data.response.content_size }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>response_time(ms)</th>
|
<th>response_time(ms)</th>
|
||||||
<td>{{ record.meta_data.response_time_ms }}</td>
|
<td>{{ record.meta_data.response.response_time_ms }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th>elapsed(ms)</th>
|
<th>elapsed(ms)</th>
|
||||||
<td>{{ record.meta_data.elapsed_ms }}</td>
|
<td>{{ record.meta_data.response.elapsed_ms }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ class TestHttpRunner(ApiServerUnittest):
|
|||||||
summary = runner.summary
|
summary = runner.summary
|
||||||
self.assertTrue(summary["success"])
|
self.assertTrue(summary["success"])
|
||||||
self.assertEqual(summary["stat"]["testsRun"], 1)
|
self.assertEqual(summary["stat"]["testsRun"], 1)
|
||||||
self.assertEqual(summary["details"][0]["records"][0]["meta_data"]["response_body"]["data"], "abc")
|
self.assertEqual(summary["details"][0]["records"][0]["meta_data"]["response"]["json"]["data"], "abc")
|
||||||
|
|
||||||
def test_html_report_repsonse_image(self):
|
def test_html_report_repsonse_image(self):
|
||||||
testset_path = "tests/httpbin/load_image.yml"
|
testset_path = "tests/httpbin/load_image.yml"
|
||||||
|
|||||||
Reference in New Issue
Block a user