mirror of
https://github.com/httprunner/httprunner.git
synced 2026-06-07 00:39:34 +08:00
refactor: add typing with pydantic
This commit is contained in:
@@ -73,15 +73,15 @@ def main_run(args):
|
|||||||
err_code = 0
|
err_code = 0
|
||||||
try:
|
try:
|
||||||
for path in args.testfile_paths:
|
for path in args.testfile_paths:
|
||||||
summary = runner.run_path(path, dot_env_path=args.dot_env_path)
|
testsuite_summary = runner.run_path(path, dot_env_path=args.dot_env_path)
|
||||||
report_dir = args.report_dir or os.path.join(os.getcwd(), "reports")
|
report_dir = args.report_dir or os.path.join(os.getcwd(), "reports")
|
||||||
gen_html_report(
|
gen_html_report(
|
||||||
summary,
|
testsuite_summary,
|
||||||
report_template=args.report_template,
|
report_template=args.report_template,
|
||||||
report_dir=report_dir,
|
report_dir=report_dir,
|
||||||
report_file=args.report_file
|
report_file=args.report_file
|
||||||
)
|
)
|
||||||
err_code |= (0 if summary and summary["success"] else 1)
|
err_code |= (0 if testsuite_summary and testsuite_summary.success else 1)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
logger.error(f"!!!!!!!!!! exception stage: {runner.exception_stage} !!!!!!!!!!\n{str(ex)}")
|
logger.error(f"!!!!!!!!!! exception stage: {runner.exception_stage} !!!!!!!!!!\n{str(ex)}")
|
||||||
err_code = 1
|
err_code = 1
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from requests.exceptions import (InvalidSchema, InvalidURL, MissingSchema,
|
|||||||
|
|
||||||
from httprunner import response
|
from httprunner import response
|
||||||
from httprunner.utils import lower_dict_keys, omit_long_data
|
from httprunner.utils import lower_dict_keys, omit_long_data
|
||||||
|
from httprunner.v3.schema import MetaData, RequestStat
|
||||||
|
|
||||||
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
||||||
|
|
||||||
@@ -107,9 +108,8 @@ class HttpSession(requests.Session):
|
|||||||
def init_meta_data(self):
|
def init_meta_data(self):
|
||||||
""" 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 = MetaData(
|
||||||
"name": "",
|
data=[
|
||||||
"data": [
|
|
||||||
{
|
{
|
||||||
"request": {
|
"request": {
|
||||||
"url": "N/A",
|
"url": "N/A",
|
||||||
@@ -124,19 +124,15 @@ class HttpSession(requests.Session):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"stat": {
|
stat=RequestStat()
|
||||||
"content_size": "N/A",
|
)
|
||||||
"response_time_ms": "N/A",
|
|
||||||
"elapsed_ms": "N/A",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def update_last_req_resp_record(self, resp_obj):
|
def update_last_req_resp_record(self, resp_obj):
|
||||||
"""
|
"""
|
||||||
update request and response info from Response() object.
|
update request and response info from Response() object.
|
||||||
"""
|
"""
|
||||||
self.meta_data["data"].pop()
|
self.meta_data.data.pop()
|
||||||
self.meta_data["data"].append(get_req_resp_record(resp_obj))
|
self.meta_data.data.append(get_req_resp_record(resp_obj))
|
||||||
|
|
||||||
def request(self, method, url, name=None, **kwargs):
|
def request(self, method, url, name=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
@@ -180,13 +176,13 @@ class HttpSession(requests.Session):
|
|||||||
self.init_meta_data()
|
self.init_meta_data()
|
||||||
|
|
||||||
# record test name
|
# record test name
|
||||||
self.meta_data["name"] = name
|
self.meta_data.name = name
|
||||||
|
|
||||||
# record original request info
|
# record original request info
|
||||||
self.meta_data["data"][0]["request"]["method"] = method
|
self.meta_data.data[0]["request"]["method"] = method
|
||||||
self.meta_data["data"][0]["request"]["url"] = url
|
self.meta_data.data[0]["request"]["url"] = url
|
||||||
kwargs.setdefault("timeout", 120)
|
kwargs.setdefault("timeout", 120)
|
||||||
self.meta_data["data"][0]["request"].update(kwargs)
|
self.meta_data.data[0]["request"].update(kwargs)
|
||||||
|
|
||||||
start_timestamp = time.time()
|
start_timestamp = time.time()
|
||||||
response = self._send_request_safe_mode(method, url, **kwargs)
|
response = self._send_request_safe_mode(method, url, **kwargs)
|
||||||
@@ -200,15 +196,13 @@ class HttpSession(requests.Session):
|
|||||||
content_size = len(response.content or "")
|
content_size = len(response.content or "")
|
||||||
|
|
||||||
# record the consumed time
|
# record the consumed time
|
||||||
self.meta_data["stat"] = {
|
self.meta_data.stat.response_time_ms = response_time_ms
|
||||||
"response_time_ms": response_time_ms,
|
self.meta_data.stat.elapsed_ms = response.elapsed.microseconds / 1000.0
|
||||||
"elapsed_ms": response.elapsed.microseconds / 1000.0,
|
self.meta_data.stat.content_size = content_size
|
||||||
"content_size": content_size
|
|
||||||
}
|
|
||||||
|
|
||||||
# record request and response histories, include 30X redirection
|
# record request and response histories, include 30X redirection
|
||||||
response_list = response.history + [response]
|
response_list = response.history + [response]
|
||||||
self.meta_data["data"] = [
|
self.meta_data.data = [
|
||||||
get_req_resp_record(resp_obj)
|
get_req_resp_record(resp_obj)
|
||||||
for resp_obj in response_list
|
for resp_obj in response_list
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -6,20 +6,21 @@ from jinja2 import Template
|
|||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
from httprunner.exceptions import SummaryEmpty
|
from httprunner.exceptions import SummaryEmpty
|
||||||
|
from httprunner.v3.schema import TestSuiteSummary
|
||||||
|
|
||||||
|
|
||||||
def gen_html_report(summary, report_template=None, report_dir=None, report_file=None):
|
def gen_html_report(testsuite_summary: TestSuiteSummary, report_template=None, report_dir=None, report_file=None):
|
||||||
""" render html report with specified report name and template
|
""" render html report with specified report name and template
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
summary (dict): test result summary data
|
testsuite_summary (dict): testsuite result summary data
|
||||||
report_template (str): specify html report template path, template should be in Jinja2 format.
|
report_template (str): specify html report template path, template should be in Jinja2 format.
|
||||||
report_dir (str): specify html report save directory
|
report_dir (str): specify html report save directory
|
||||||
report_file (str): specify html report file path, this has higher priority than specifying report dir.
|
report_file (str): specify html report file path, this has higher priority than specifying report dir.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if not summary["time"] or summary["stat"]["testcases"]["total"] == 0:
|
if not testsuite_summary.time or testsuite_summary.stat.testcases["total"] == 0:
|
||||||
logger.error(f"test result summary is empty ! {summary}")
|
logger.error(f"test result testsuite_summary is empty ! {testsuite_summary}")
|
||||||
raise SummaryEmpty
|
raise SummaryEmpty
|
||||||
|
|
||||||
if not report_template:
|
if not report_template:
|
||||||
@@ -33,9 +34,9 @@ def gen_html_report(summary, report_template=None, report_dir=None, report_file=
|
|||||||
|
|
||||||
logger.info("Start to render Html report ...")
|
logger.info("Start to render Html report ...")
|
||||||
|
|
||||||
start_at_timestamp = summary["time"]["start_at"]
|
start_at_timestamp = testsuite_summary.time.start_at
|
||||||
utc_time_iso_8601_str = datetime.utcfromtimestamp(start_at_timestamp).isoformat()
|
utc_time_iso_8601_str = datetime.utcfromtimestamp(start_at_timestamp).isoformat()
|
||||||
summary["time"]["start_datetime"] = utc_time_iso_8601_str
|
testsuite_summary.time.start_datetime = utc_time_iso_8601_str
|
||||||
|
|
||||||
if report_file:
|
if report_file:
|
||||||
report_dir = os.path.dirname(report_file)
|
report_dir = os.path.dirname(report_file)
|
||||||
@@ -55,7 +56,7 @@ def gen_html_report(summary, report_template=None, report_dir=None, report_file=
|
|||||||
rendered_content = Template(
|
rendered_content = Template(
|
||||||
template_content,
|
template_content,
|
||||||
extensions=["jinja2.ext.loopcontrols"]
|
extensions=["jinja2.ext.loopcontrols"]
|
||||||
).render(summary)
|
).render(testsuite_summary.dict())
|
||||||
fp_w.write(rendered_content)
|
fp_w.write(rendered_content)
|
||||||
|
|
||||||
logger.info(f"Generated Html report: {report_path}")
|
logger.info(f"Generated Html report: {report_path}")
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import unittest
|
|||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
|
from httprunner.v3.schema import Record
|
||||||
|
|
||||||
|
|
||||||
class HtmlTestResult(unittest.TextTestResult):
|
class HtmlTestResult(unittest.TextTestResult):
|
||||||
""" A html result class that can generate formatted html results.
|
""" A html result class that can generate formatted html results.
|
||||||
@@ -13,13 +15,13 @@ class HtmlTestResult(unittest.TextTestResult):
|
|||||||
self.records = []
|
self.records = []
|
||||||
|
|
||||||
def _record_test(self, test, status, attachment=''):
|
def _record_test(self, test, status, attachment=''):
|
||||||
data = {
|
record = Record(
|
||||||
'name': test.shortDescription(),
|
name=test.shortDescription(),
|
||||||
'status': status,
|
status=status,
|
||||||
'attachment': attachment,
|
attachment=attachment,
|
||||||
"meta_datas": test.meta_datas
|
meta_datas=test.meta_datas
|
||||||
}
|
)
|
||||||
self.records.append(data)
|
self.records.append(record)
|
||||||
|
|
||||||
def startTestRun(self):
|
def startTestRun(self):
|
||||||
self.start_at = time.time()
|
self.start_at = time.time()
|
||||||
|
|||||||
@@ -201,7 +201,7 @@
|
|||||||
|
|
||||||
{% for record in test_suite_summary.records %}
|
{% for record in test_suite_summary.records %}
|
||||||
{% set record_index = "{}_{}".format(suite_index, loop.index) %}
|
{% set record_index = "{}_{}".format(suite_index, loop.index) %}
|
||||||
{% set record_meta_datas = record.meta_datas_expanded %}
|
{% set record_meta_datas = record.meta_datas %}
|
||||||
<tr id="record_{{record_index}}">
|
<tr id="record_{{record_index}}">
|
||||||
<th class="{{record.status}}" style="width:5em;">{{record.status}}</th>
|
<th class="{{record.status}}" style="width:5em;">{{record.status}}</th>
|
||||||
<td colspan="2">{{record.name}}</td>
|
<td colspan="2">{{record.name}}</td>
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
import json
|
import json
|
||||||
from base64 import b64encode
|
from base64 import b64encode
|
||||||
from collections import Iterable
|
from collections import Iterable
|
||||||
|
from typing import List
|
||||||
|
|
||||||
from jinja2 import escape
|
from jinja2 import escape
|
||||||
from requests.cookies import RequestsCookieJar
|
from requests.cookies import RequestsCookieJar
|
||||||
|
|
||||||
|
from httprunner.v3.schema import TestSuiteSummary, MetaData
|
||||||
|
|
||||||
|
|
||||||
def dumps_json(value):
|
def dumps_json(value):
|
||||||
""" dumps json value to indented string
|
""" dumps json value to indented string
|
||||||
@@ -164,20 +167,20 @@ def __expand_meta_datas(meta_datas, meta_datas_expanded):
|
|||||||
[dict1, dict2, dict3]
|
[dict1, dict2, dict3]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if isinstance(meta_datas, dict):
|
if isinstance(meta_datas, MetaData):
|
||||||
meta_datas_expanded.append(meta_datas)
|
meta_datas_expanded.append(meta_datas)
|
||||||
elif isinstance(meta_datas, list):
|
elif isinstance(meta_datas, list):
|
||||||
for meta_data in meta_datas:
|
for meta_data in meta_datas:
|
||||||
__expand_meta_datas(meta_data, meta_datas_expanded)
|
__expand_meta_datas(meta_data, meta_datas_expanded)
|
||||||
|
|
||||||
|
|
||||||
def __get_total_response_time(meta_datas_expanded):
|
def __get_total_response_time(meta_datas: List[MetaData]):
|
||||||
""" caculate total response time of all meta_datas
|
""" caculate total response time of all meta_datas
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
response_time = 0
|
response_time = 0
|
||||||
for meta_data in meta_datas_expanded:
|
for meta_data in meta_datas:
|
||||||
response_time += meta_data["stat"]["response_time_ms"]
|
response_time += meta_data.stat.response_time_ms
|
||||||
|
|
||||||
return "{:.2f}".format(response_time)
|
return "{:.2f}".format(response_time)
|
||||||
|
|
||||||
@@ -186,30 +189,24 @@ def __get_total_response_time(meta_datas_expanded):
|
|||||||
return "N/A"
|
return "N/A"
|
||||||
|
|
||||||
|
|
||||||
def __stringify_meta_datas(meta_datas):
|
def __stringify_meta_datas(meta_datas: List[MetaData]):
|
||||||
|
|
||||||
if isinstance(meta_datas, list):
|
for meta_data in meta_datas:
|
||||||
for _meta_data in meta_datas:
|
data_list = meta_data.data
|
||||||
__stringify_meta_datas(_meta_data)
|
|
||||||
elif isinstance(meta_datas, dict):
|
|
||||||
data_list = meta_datas["data"]
|
|
||||||
for data in data_list:
|
for data in data_list:
|
||||||
__stringify_request(data["request"])
|
__stringify_request(data["request"])
|
||||||
__stringify_response(data["response"])
|
__stringify_response(data["response"])
|
||||||
|
|
||||||
|
|
||||||
def stringify_summary(summary):
|
def stringify_summary(testsuite_summary: TestSuiteSummary):
|
||||||
""" stringify summary, in order to dump json file and generate html report.
|
""" stringify summary, in order to dump json file and generate html report.
|
||||||
"""
|
"""
|
||||||
for index, suite_summary in enumerate(summary["details"]):
|
for index, testcase_summary in enumerate(testsuite_summary.details):
|
||||||
|
|
||||||
if not suite_summary.get("name"):
|
if not testcase_summary.name:
|
||||||
suite_summary["name"] = f"testcase {index}"
|
testcase_summary.name = f"testcase {index}"
|
||||||
|
|
||||||
for record in suite_summary.get("records"):
|
for record in testcase_summary.records:
|
||||||
meta_datas = record['meta_datas']
|
meta_datas = record.meta_datas
|
||||||
__stringify_meta_datas(meta_datas)
|
__stringify_meta_datas(meta_datas)
|
||||||
meta_datas_expanded = []
|
record.response_time = __get_total_response_time(meta_datas)
|
||||||
__expand_meta_datas(meta_datas, meta_datas_expanded)
|
|
||||||
record["meta_datas_expanded"] = meta_datas_expanded
|
|
||||||
record["response_time"] = __get_total_response_time(meta_datas_expanded)
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import platform
|
import platform
|
||||||
|
|
||||||
from httprunner import __version__
|
from httprunner import __version__
|
||||||
|
from httprunner.report.html.result import HtmlTestResult
|
||||||
|
from httprunner.v3.schema import TestCaseSummary, TestCaseStat, TestCaseTime, TestCaseInOut
|
||||||
|
|
||||||
|
|
||||||
def get_platform():
|
def get_platform():
|
||||||
@@ -38,7 +40,7 @@ def aggregate_stat(origin_stat, new_stat):
|
|||||||
origin_stat[key] += new_stat[key]
|
origin_stat[key] += new_stat[key]
|
||||||
|
|
||||||
|
|
||||||
def get_summary(result):
|
def get_summary(result: HtmlTestResult) -> TestCaseSummary:
|
||||||
""" get summary from test result
|
""" get summary from test result
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -55,28 +57,20 @@ def get_summary(result):
|
|||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
summary = {
|
return TestCaseSummary(
|
||||||
"success": result.wasSuccessful(),
|
success=result.wasSuccessful(),
|
||||||
"stat": {
|
stat=TestCaseStat(
|
||||||
'total': result.testsRun,
|
total=result.testsRun,
|
||||||
'failures': len(result.failures),
|
failures=len(result.failures),
|
||||||
'errors': len(result.errors),
|
errors=len(result.errors),
|
||||||
'skipped': len(result.skipped),
|
skipped=len(result.skipped),
|
||||||
'expectedFailures': len(result.expectedFailures),
|
expectedFailures=len(result.expectedFailures),
|
||||||
'unexpectedSuccesses': len(result.unexpectedSuccesses)
|
unexpectedSuccesses=len(result.unexpectedSuccesses)
|
||||||
}
|
),
|
||||||
}
|
time=TestCaseTime(
|
||||||
summary["stat"]["successes"] = summary["stat"]["total"] \
|
start_at=result.start_at,
|
||||||
- summary["stat"]["failures"] \
|
duration=result.duration
|
||||||
- summary["stat"]["errors"] \
|
),
|
||||||
- summary["stat"]["skipped"] \
|
records=result.records,
|
||||||
- summary["stat"]["expectedFailures"] \
|
in_out=TestCaseInOut()
|
||||||
- summary["stat"]["unexpectedSuccesses"]
|
)
|
||||||
|
|
||||||
summary["time"] = {
|
|
||||||
'start_at': result.start_at,
|
|
||||||
'duration': result.duration
|
|
||||||
}
|
|
||||||
summary["records"] = result.records
|
|
||||||
|
|
||||||
return summary
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from loguru import logger
|
|||||||
|
|
||||||
from httprunner import report, loader, utils, exceptions, __version__
|
from httprunner import report, loader, utils, exceptions, __version__
|
||||||
from httprunner.v3.runner import TestCaseRunner
|
from httprunner.v3.runner import TestCaseRunner
|
||||||
from httprunner.v3.schema import TestsMapping
|
from httprunner.v3.schema import TestsMapping, TestCaseSummary, TestSuiteSummary
|
||||||
|
|
||||||
|
|
||||||
class HttpRunner(object):
|
class HttpRunner(object):
|
||||||
@@ -91,10 +91,10 @@ class HttpRunner(object):
|
|||||||
|
|
||||||
return prepared_testcases
|
return prepared_testcases
|
||||||
|
|
||||||
def _run_suite(self, prepared_testcases: List[unittest.TestSuite]) -> List[Dict]:
|
def _run_suite(self, prepared_testcases: List[unittest.TestSuite]) -> List[TestCaseSummary]:
|
||||||
""" run prepared testcases
|
""" run prepared testcases
|
||||||
"""
|
"""
|
||||||
tests_results: List[Dict] = []
|
tests_results: List[TestCaseSummary] = []
|
||||||
|
|
||||||
for index, testcase in enumerate(prepared_testcases):
|
for index, testcase in enumerate(prepared_testcases):
|
||||||
log_handler = None
|
log_handler = None
|
||||||
@@ -108,18 +108,16 @@ class HttpRunner(object):
|
|||||||
|
|
||||||
result = self.unittest_runner.run(testcase)
|
result = self.unittest_runner.run(testcase)
|
||||||
testcase_summary = report.get_summary(result)
|
testcase_summary = report.get_summary(result)
|
||||||
testcase_summary["name"] = testcase.config.name
|
testcase_summary.name = testcase.config.name
|
||||||
testcase_summary["in_out"] = {
|
testcase_summary.in_out.vars = testcase.config.variables
|
||||||
"in": testcase.config.variables,
|
testcase_summary.in_out.out = testcase.config.export
|
||||||
"out": testcase.config.export
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.save_tests and log_handler:
|
if self.save_tests and log_handler:
|
||||||
logger.remove(log_handler)
|
logger.remove(log_handler)
|
||||||
logs_file_abs_path = utils.prepare_log_file_abs_path(
|
logs_file_abs_path = utils.prepare_log_file_abs_path(
|
||||||
self.test_path, f"testcase_{index+1}.log"
|
self.test_path, f"testcase_{index+1}.log"
|
||||||
)
|
)
|
||||||
testcase_summary["log"] = logs_file_abs_path
|
testcase_summary.log = logs_file_abs_path
|
||||||
|
|
||||||
if result.wasSuccessful():
|
if result.wasSuccessful():
|
||||||
tests_results.append(testcase_summary)
|
tests_results.append(testcase_summary)
|
||||||
@@ -128,14 +126,14 @@ class HttpRunner(object):
|
|||||||
|
|
||||||
return tests_results
|
return tests_results
|
||||||
|
|
||||||
def _aggregate(self, tests_results: List[Dict]):
|
def _aggregate(self, tests_results: List[TestCaseSummary]) -> TestSuiteSummary:
|
||||||
""" aggregate multiple testcase results
|
""" aggregate multiple testcase results
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
tests_results (list): list of testcase summary
|
tests_results (list): list of testcase summary
|
||||||
|
|
||||||
"""
|
"""
|
||||||
summary = {
|
testsuite_summary = {
|
||||||
"success": True,
|
"success": True,
|
||||||
"stat": {
|
"stat": {
|
||||||
"testcases": {
|
"testcases": {
|
||||||
@@ -151,21 +149,21 @@ class HttpRunner(object):
|
|||||||
}
|
}
|
||||||
|
|
||||||
for testcase_summary in tests_results:
|
for testcase_summary in tests_results:
|
||||||
if testcase_summary["success"]:
|
if testcase_summary.success:
|
||||||
summary["stat"]["testcases"]["success"] += 1
|
testsuite_summary["stat"]["testcases"]["success"] += 1
|
||||||
else:
|
else:
|
||||||
summary["stat"]["testcases"]["fail"] += 1
|
testsuite_summary["stat"]["testcases"]["fail"] += 1
|
||||||
|
|
||||||
summary["success"] &= testcase_summary["success"]
|
testsuite_summary["success"] &= testcase_summary.success
|
||||||
|
|
||||||
report.aggregate_stat(summary["stat"]["teststeps"], testcase_summary["stat"])
|
report.aggregate_stat(testsuite_summary["stat"]["teststeps"], testcase_summary.stat.dict())
|
||||||
report.aggregate_stat(summary["time"], testcase_summary["time"])
|
report.aggregate_stat(testsuite_summary["time"], testcase_summary.time.dict())
|
||||||
|
|
||||||
summary["details"].append(testcase_summary)
|
testsuite_summary["details"].append(testcase_summary)
|
||||||
|
|
||||||
return summary
|
return TestSuiteSummary.parse_obj(testsuite_summary)
|
||||||
|
|
||||||
def run_tests(self, tests_mapping):
|
def run_tests(self, tests_mapping) -> TestSuiteSummary:
|
||||||
""" run testcase/testsuite data
|
""" run testcase/testsuite data
|
||||||
"""
|
"""
|
||||||
tests = TestsMapping.parse_obj(tests_mapping)
|
tests = TestsMapping.parse_obj(tests_mapping)
|
||||||
@@ -195,7 +193,7 @@ class HttpRunner(object):
|
|||||||
|
|
||||||
if self.save_tests:
|
if self.save_tests:
|
||||||
utils.dump_json_file(
|
utils.dump_json_file(
|
||||||
self._summary,
|
self._summary.dict(),
|
||||||
utils.prepare_log_file_abs_path(self.test_path, "summary.json")
|
utils.prepare_log_file_abs_path(self.test_path, "summary.json")
|
||||||
)
|
)
|
||||||
# save variables and export data
|
# save variables and export data
|
||||||
@@ -207,7 +205,7 @@ class HttpRunner(object):
|
|||||||
|
|
||||||
return self._summary
|
return self._summary
|
||||||
|
|
||||||
def run_path(self, path, dot_env_path=None, mapping=None):
|
def run_path(self, path, dot_env_path=None, mapping=None) -> TestSuiteSummary:
|
||||||
""" run testcase/testsuite file or folder.
|
""" run testcase/testsuite file or folder.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class ResponseObject(object):
|
|||||||
"headers": resp_obj.headers,
|
"headers": resp_obj.headers,
|
||||||
"body": resp_obj.json()
|
"body": resp_obj.json()
|
||||||
}
|
}
|
||||||
self.validation_results = {}
|
self.validation_results: Dict = {}
|
||||||
|
|
||||||
def __getattr__(self, key):
|
def __getattr__(self, key):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from typing import List
|
from typing import List, Dict
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@ from httprunner.client import HttpSession
|
|||||||
from httprunner.exceptions import ValidationFailure
|
from httprunner.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
|
||||||
from httprunner.v3.response import ResponseObject
|
from httprunner.v3.response import ResponseObject
|
||||||
from httprunner.v3.schema import TestsConfig, TestStep, VariablesMapping, TestCase
|
from httprunner.v3.schema import TestsConfig, TestStep, VariablesMapping, TestCase, MetaData
|
||||||
|
|
||||||
|
|
||||||
class TestCaseRunner(object):
|
class TestCaseRunner(object):
|
||||||
@@ -15,8 +15,8 @@ class TestCaseRunner(object):
|
|||||||
config: TestsConfig = {}
|
config: TestsConfig = {}
|
||||||
teststeps: List[TestStep] = []
|
teststeps: List[TestStep] = []
|
||||||
session: HttpSession = None
|
session: HttpSession = None
|
||||||
meta_datas: List = []
|
meta_datas: List[MetaData] = []
|
||||||
validation_results: List = []
|
validation_results: Dict = {}
|
||||||
|
|
||||||
def init(self, testcase: TestCase) -> "TestCaseRunner":
|
def init(self, testcase: TestCase) -> "TestCaseRunner":
|
||||||
self.config = testcase.config
|
self.config = testcase.config
|
||||||
@@ -92,8 +92,8 @@ class TestCaseRunner(object):
|
|||||||
finally:
|
finally:
|
||||||
self.validation_results = resp_obj.validation_results
|
self.validation_results = resp_obj.validation_results
|
||||||
# save request & response meta data
|
# save request & response meta data
|
||||||
self.session.meta_data["validators"] = self.validation_results
|
self.session.meta_data.validators = self.validation_results
|
||||||
self.session.meta_data["name"] = step.name
|
self.session.meta_data.name = step.name
|
||||||
self.meta_datas.append(self.session.meta_data)
|
self.meta_datas.append(self.session.meta_data)
|
||||||
|
|
||||||
return extract_mapping
|
return extract_mapping
|
||||||
|
|||||||
@@ -80,3 +80,74 @@ class ProjectMeta(BaseModel):
|
|||||||
class TestsMapping(BaseModel):
|
class TestsMapping(BaseModel):
|
||||||
project_mapping: ProjectMeta # TODO: rename to project_meta
|
project_mapping: ProjectMeta # TODO: rename to project_meta
|
||||||
testcases: List[TestCase]
|
testcases: List[TestCase]
|
||||||
|
|
||||||
|
|
||||||
|
class Stat(BaseModel):
|
||||||
|
testcases: Dict
|
||||||
|
teststeps: Dict
|
||||||
|
|
||||||
|
|
||||||
|
class TestCaseTime(BaseModel):
|
||||||
|
start_at: float
|
||||||
|
duration: float
|
||||||
|
start_datetime: Text = ""
|
||||||
|
|
||||||
|
|
||||||
|
class TestCaseStat(BaseModel):
|
||||||
|
total: int = 0
|
||||||
|
successes: int = 0
|
||||||
|
failures: int = 0
|
||||||
|
errors: int = 0
|
||||||
|
skipped: int = 0
|
||||||
|
expectedFailures: int = 0
|
||||||
|
unexpectedSuccesses: int = 0
|
||||||
|
|
||||||
|
|
||||||
|
class TestCaseInOut(BaseModel):
|
||||||
|
vars: VariablesMapping = {}
|
||||||
|
out: Export = []
|
||||||
|
|
||||||
|
|
||||||
|
class RequestStat(BaseModel):
|
||||||
|
content_size: Text = "N/A"
|
||||||
|
response_time_ms: Text = "N/A"
|
||||||
|
elapsed_ms: Text = "N/A"
|
||||||
|
|
||||||
|
|
||||||
|
class MetaData(BaseModel):
|
||||||
|
name: Text = ""
|
||||||
|
data: List[Dict]
|
||||||
|
stat: RequestStat
|
||||||
|
validators: Dict = {}
|
||||||
|
|
||||||
|
|
||||||
|
class Record(BaseModel):
|
||||||
|
name: Text = ""
|
||||||
|
status: Text = ""
|
||||||
|
attachment: Text = ""
|
||||||
|
meta_datas: List[MetaData] = []
|
||||||
|
response_time: Text = "N/A"
|
||||||
|
|
||||||
|
|
||||||
|
class TestCaseSummary(BaseModel):
|
||||||
|
name: Text = ""
|
||||||
|
success: bool
|
||||||
|
stat: TestCaseStat
|
||||||
|
time: TestCaseTime
|
||||||
|
records: List = [Record]
|
||||||
|
in_out: TestCaseInOut = {}
|
||||||
|
log: Text = ""
|
||||||
|
|
||||||
|
|
||||||
|
class PlatformInfo(BaseModel):
|
||||||
|
httprunner_version: Text
|
||||||
|
python_version: Text
|
||||||
|
platform: Text
|
||||||
|
|
||||||
|
|
||||||
|
class TestSuiteSummary(BaseModel):
|
||||||
|
success: bool
|
||||||
|
stat: Stat
|
||||||
|
time: TestCaseTime
|
||||||
|
platform: PlatformInfo
|
||||||
|
details: List[TestCaseSummary]
|
||||||
|
|||||||
Reference in New Issue
Block a user