diff --git a/httprunner/report/__init__.py b/httprunner/report/__init__.py
deleted file mode 100644
index 9599cc3d..00000000
--- a/httprunner/report/__init__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-"""
-HttpRunner report
-
-- summarize: aggregate test stat data to summary
-- stringify: stringify summary, in order to dump json file and generate html report.
-- html: render html report
-"""
-
-from httprunner.report.summarize import get_platform, aggregate_stat, get_summary
-from httprunner.report.stringify import stringify_summary
-from httprunner.report.html import HtmlTestResult, gen_html_report
-
-__all__ = [
- "get_platform",
- "aggregate_stat",
- "get_summary",
- "stringify_summary",
- "HtmlTestResult",
- "gen_html_report",
-]
diff --git a/httprunner/report/html/__init__.py b/httprunner/report/html/__init__.py
deleted file mode 100644
index 398adeea..00000000
--- a/httprunner/report/html/__init__.py
+++ /dev/null
@@ -1,12 +0,0 @@
-"""
-HttpRunner html report
-
-- result: define resultclass for unittest TextTestRunner
-- gen_report: render html report with jinja2 template
-
-"""
-
-from httprunner.report.html.result import HtmlTestResult
-from httprunner.report.html.gen_report import gen_html_report
-
-__all__ = ["HtmlTestResult", "gen_html_report"]
diff --git a/httprunner/report/html/gen_report.py b/httprunner/report/html/gen_report.py
deleted file mode 100644
index 8d7364d9..00000000
--- a/httprunner/report/html/gen_report.py
+++ /dev/null
@@ -1,64 +0,0 @@
-import io
-import os
-
-from jinja2 import Template
-from loguru import logger
-
-from httprunner.exceptions import SummaryEmpty
-from httprunner.schema import TestSuiteSummary
-
-
-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
-
- Args:
- testsuite_summary (dict): testsuite result summary data
- report_template (str): specify html report template path, template should be in Jinja2 format.
- report_dir (str): specify html report save directory
- report_file (str): specify html report file path, this has higher priority than specifying report dir.
-
- """
- if testsuite_summary.stat.total == 0:
- logger.error(f"test result testsuite_summary is empty ! {testsuite_summary}")
- raise SummaryEmpty
-
- if not report_template:
- report_template = os.path.join(
- os.path.abspath(os.path.dirname(__file__)), "template.html"
- )
- logger.debug("No html report template specified, use default.")
- else:
- logger.info(f"render with html report template: {report_template}")
-
- logger.info("Start to render Html report ...")
-
- if report_file:
- report_dir = os.path.dirname(report_file)
- report_file_name = os.path.basename(report_file)
- else:
- report_dir = report_dir or os.path.join(os.getcwd(), "reports")
- # fix #826: Windows does not support file name include ":"
- report_file_name = "{}.html".format(
- testsuite_summary.time.start_at_iso_format.replace(":", "").replace("-", "")
- )
-
- if not os.path.isdir(report_dir):
- os.makedirs(report_dir)
-
- report_path = os.path.join(report_dir, report_file_name)
- with io.open(report_template, "r", encoding="utf-8") as fp_r:
- template_content = fp_r.read()
- with io.open(report_path, "w", encoding="utf-8") as fp_w:
- rendered_content = Template(
- template_content, extensions=["jinja2.ext.loopcontrols"]
- ).render(testsuite_summary.dict())
- fp_w.write(rendered_content)
-
- logger.info(f"Generated Html report: {report_path}")
-
- return report_path
diff --git a/httprunner/report/html/result.py b/httprunner/report/html/result.py
deleted file mode 100644
index 7d60f1e4..00000000
--- a/httprunner/report/html/result.py
+++ /dev/null
@@ -1,65 +0,0 @@
-import time
-import unittest
-
-from loguru import logger
-
-
-class HtmlTestResult(unittest.TextTestResult):
- """ A html result class that can generate formatted html results, used by TextTestRunner.
- Each testcase is corresponding to one HtmlTestResult instance
- """
-
- def __init__(self, stream, descriptions, verbosity):
- super(HtmlTestResult, self).__init__(stream, descriptions, verbosity)
- self.name = ""
- self.status = ""
- self.attachment = ""
- self.step_datas = None
-
- def _record_test(self, test, status, attachment=""):
- self.name = test.shortDescription()
- self.status = status
- self.attachment = attachment
- self.step_datas = test.step_datas
-
- def startTestRun(self):
- self.start_at = time.time()
-
- def startTest(self, test):
- """ add start test time """
- super(HtmlTestResult, self).startTest(test)
- logger.info(test.shortDescription())
-
- def addSuccess(self, test):
- super(HtmlTestResult, self).addSuccess(test)
- self._record_test(test, "success")
- print("")
-
- def addError(self, test, err):
- super(HtmlTestResult, self).addError(test, err)
- self._record_test(test, "error", self._exc_info_to_string(err, test))
- print("")
-
- def addFailure(self, test, err):
- super(HtmlTestResult, self).addFailure(test, err)
- self._record_test(test, "failure", self._exc_info_to_string(err, test))
- print("")
-
- def addSkip(self, test, reason):
- super(HtmlTestResult, self).addSkip(test, reason)
- self._record_test(test, "skipped", reason)
- print("")
-
- def addExpectedFailure(self, test, err):
- super(HtmlTestResult, self).addExpectedFailure(test, err)
- self._record_test(test, "ExpectedFailure", self._exc_info_to_string(err, test))
- print("")
-
- def addUnexpectedSuccess(self, test):
- super(HtmlTestResult, self).addUnexpectedSuccess(test)
- self._record_test(test, "UnexpectedSuccess")
- print("")
-
- @property
- def duration(self):
- return time.time() - self.start_at
diff --git a/httprunner/report/html/template.html b/httprunner/report/html/template.html
deleted file mode 100644
index 76baaa8b..00000000
--- a/httprunner/report/html/template.html
+++ /dev/null
@@ -1,350 +0,0 @@
-
-
-
- {{html_report_name}} - TestReport
-
-
-
-
- Test Report: {{html_report_name}}
-
- Summary
-
-
- | START AT |
- {{time.start_at_iso_format}} |
-
-
- | DURATION |
- {{ '%0.3f'| format(time.duration|float) }} seconds |
-
-
- | PLATFORM |
- HttpRunner {{ platform.httprunner_version }} |
- {{ platform.python_version }} |
- {{ platform.platform }} |
-
-
- | STAT |
- SUCCESS |
- FAIL |
-
-
- | total (details) => |
- {{stat.success}} |
- {{stat.fail}} |
-
-
-
- Details
-
- {% for testcase_summary in testcases %}
- {% set testcase_index = loop.index %}
- {{testcase_summary.name}}
-
-
- | Status |
- Name |
- Response Time |
- Detail |
-
-
- {% set testcase = testcase_summary %}
- {% set step_datas = testcase_summary.step_datas %}
- {% for session_data in step_datas %}
- {% set step_index = "{}_{}".format(testcase_index, loop.index) %}
-
- | {{session_data.status}} |
- {{session_data.name}} |
- {{ session_data.stat.response_time_ms }} ms |
-
- log-{{loop.index}}
-
-
- {% if session_data.status == "failed" %}
- traceback
-
- {% endif %}
-
- |
-
- {% endfor %}
-
- {% endfor %}
-
\ No newline at end of file
diff --git a/httprunner/report/report.py b/httprunner/report/report.py
deleted file mode 100644
index b69f4be8..00000000
--- a/httprunner/report/report.py
+++ /dev/null
@@ -1,45 +0,0 @@
-import json
-import platform
-import time
-import uuid
-
-import requests
-
-from httprunner import __version__
-
-
-def prepare_event_kwargs(event_name, params):
- """ prepare report event kwargs"""
-
- kwargs = {
- "headers": {"content-type": "application/json"},
- "json": {
- "user": {"user_unique_id": str(uuid.getnode())},
- "header": {
- "app_id": 173519,
- "os_name": platform.system(),
- "os_version": platform.release(),
- "app_version": __version__, # HttpRunner version
- },
- "events": [
- {
- "event": event_name,
- "params": json.dumps(params),
- "time": int(time.time()),
- }
- ],
- "verbose": 1,
- },
- }
- return kwargs
-
-
-def report_event(event_name, success=True):
- params = {"success": 1 if success else 0}
- kwargs = prepare_event_kwargs(event_name, params)
- resp = requests.post("http://mcs.snssdk.com/v1/json", **kwargs)
- print("resp---", resp.json())
-
-
-if __name__ == "__main__":
- report_event("loader")
diff --git a/httprunner/report/stringify.py b/httprunner/report/stringify.py
deleted file mode 100644
index 89e32801..00000000
--- a/httprunner/report/stringify.py
+++ /dev/null
@@ -1,170 +0,0 @@
-import json
-from base64 import b64encode
-from collections import Iterable
-from typing import List
-
-from jinja2 import escape
-from requests.cookies import RequestsCookieJar
-
-from httprunner.schema import TestSuiteSummary, SessionData
-
-
-def dumps_json(value):
- """ dumps json value to indented string
-
- Args:
- value (dict): raw json data
-
- Returns:
- str: indented json dump string
-
- """
- return json.dumps(value, indent=2, ensure_ascii=False)
-
-
-def detect_encoding(value):
- try:
- return json.detect_encoding(value)
- except AttributeError:
- return "utf-8"
-
-
-def __stringify_request(request_data):
- """ stringfy HTTP request data
-
- Args:
- request_data (dict): HTTP request data in dict.
-
- {
- "url": "http://127.0.0.1:5000/api/get-token",
- "method": "POST",
- "headers": {
- "User-Agent": "python-requests/2.20.0",
- "Accept-Encoding": "gzip, deflate",
- "Accept": "*/*",
- "Connection": "keep-alive",
- "user_agent": "iOS/10.3",
- "device_sn": "TESTCASE_CREATE_XXX",
- "os_platform": "ios",
- "app_version": "2.8.6",
- "Content-Type": "application/json",
- "Content-Length": "52"
- },
- "body": b'{"sign": "cb9d60acd09080ea66c8e63a1c78c6459ea00168"}',
- "verify": false
- }
-
- """
- for key, value in request_data.items():
-
- if isinstance(value, (list, dict)):
- value = dumps_json(value)
-
- elif isinstance(value, bytes):
- try:
- encoding = detect_encoding(value)
- value = value.decode(encoding)
- if key == "body":
- try:
- # request body is in json format
- value = json.loads(value)
- value = dumps_json(value)
- except json.JSONDecodeError:
- pass
- value = escape(value)
- except UnicodeDecodeError:
- pass
-
- elif not isinstance(value, (str, bytes, int, float, Iterable)):
- # class instance, e.g. MultipartEncoder()
- value = repr(value)
-
- elif isinstance(value, RequestsCookieJar):
- value = value.get_dict()
-
- request_data[key] = value
-
-
-def __stringify_response(response_data):
- """ stringfy HTTP response data
-
- Args:
- response_data (dict):
-
- {
- "status_code": 404,
- "headers": {
- "Content-Type": "application/json",
- "Content-Length": "30",
- "Server": "Werkzeug/0.14.1 Python/3.7.0",
- "Date": "Tue, 27 Nov 2018 06:19:27 GMT"
- },
- "encoding": "None",
- "content_type": "application/json",
- "ok": false,
- "url": "http://127.0.0.1:5000/api/users/9001",
- "reason": "NOT FOUND",
- "cookies": {},
- "body": {
- "success": false,
- "data": {}
- }
- }
-
- """
- for key, value in response_data.items():
-
- if isinstance(value, (list, dict)):
- value = dumps_json(value)
-
- elif isinstance(value, bytes):
- try:
- encoding = response_data.get("encoding")
- if not encoding or encoding == "None":
- encoding = detect_encoding(value)
-
- if key == "body" and "image" in response_data["content_type"]:
- # display image
- value = "data:{};base64,{}".format(
- response_data["content_type"], b64encode(value).decode(encoding)
- )
- else:
- value = escape(value.decode(encoding))
- except UnicodeDecodeError:
- pass
-
- elif not isinstance(value, (str, bytes, int, float, Iterable)):
- # class instance, e.g. MultipartEncoder()
- value = repr(value)
-
- elif isinstance(value, RequestsCookieJar):
- value = value.get_dict()
-
- response_data[key] = value
-
-
-def stringify_summary(testsuite_summary: TestSuiteSummary):
- """ stringify summary, in order to dump json file and generate html report.
- """
- for index, testcase_summary in enumerate(testsuite_summary.testcases):
-
- if not testcase_summary.name:
- testcase_summary.name = f"testcase {index}"
-
- step_datas = testcase_summary.step_datas
- for step_data in step_datas:
- if isinstance(step_data.data, list):
- # List[SessionData]
- session_data_list: List[SessionData] = step_data.data
- for session_data in session_data_list:
- req_resp_list = session_data.req_resps
- for req_resp in req_resp_list:
- __stringify_request(req_resp.request)
- __stringify_response(req_resp.response)
- else:
- # SessionData
- session_data: SessionData = step_data.data
- req_resp_list = session_data.req_resps
- for req_resp in req_resp_list:
- __stringify_request(req_resp.request)
- __stringify_response(req_resp.response)
diff --git a/httprunner/report/summarize.py b/httprunner/report/summarize.py
deleted file mode 100644
index 4819b7ea..00000000
--- a/httprunner/report/summarize.py
+++ /dev/null
@@ -1,76 +0,0 @@
-import platform
-from datetime import datetime
-
-from httprunner import __version__
-from httprunner.report.html.result import HtmlTestResult
-from httprunner.schema import TestCaseSummary, TestCaseTime, TestCaseInOut
-
-
-def get_platform():
- return {
- "httprunner_version": __version__,
- "python_version": "{} {}".format(
- platform.python_implementation(), platform.python_version()
- ),
- "platform": platform.platform(),
- }
-
-
-def aggregate_stat(origin_stat, new_stat):
- """ aggregate new_stat to origin_stat.
-
- Args:
- origin_stat (dict): origin stat dict, will be updated with new_stat dict.
- new_stat (dict): new stat dict.
-
- """
- for key in new_stat:
- if key not in origin_stat:
- origin_stat[key] = new_stat[key]
- elif key == "start_at":
- # start datetime
- origin_stat["start_at"] = min(origin_stat["start_at"], new_stat["start_at"])
- elif key == "duration":
- # duration = max_end_time - min_start_time
- max_end_time = max(
- origin_stat["start_at"] + origin_stat["duration"],
- new_stat["start_at"] + new_stat["duration"],
- )
- min_start_time = min(origin_stat["start_at"], new_stat["start_at"])
- origin_stat["duration"] = max_end_time - min_start_time
- else:
- origin_stat[key] += new_stat[key]
-
-
-def get_summary(result: HtmlTestResult) -> TestCaseSummary:
- """ get summary from test result
-
- Args:
- result (instance): HtmlTestResult() instance
-
- Returns:
- dict: summary extracted from result.
-
- {
- "success": True,
- "stat": {},
- "time": {},
- "record": {}
- }
-
- """
- start_at_timestamp = result.start_at
- start_at_iso_format = datetime.utcfromtimestamp(start_at_timestamp).isoformat()
- return TestCaseSummary(
- success=result.wasSuccessful(),
- time=TestCaseTime(
- start_at=result.start_at,
- start_at_iso_format=start_at_iso_format,
- duration=result.duration,
- ),
- name=result.name,
- status=result.status,
- attachment=result.attachment,
- in_out=TestCaseInOut(),
- step_datas=result.step_datas,
- )
diff --git a/httprunner/runner.py b/httprunner/runner.py
index bcb8d9c4..3c540836 100644
--- a/httprunner/runner.py
+++ b/httprunner/runner.py
@@ -174,7 +174,7 @@ class HttpRunner(object):
self.start_at = time.time()
self.step_datas: List[StepData] = []
- self.session_variables.clear()
+ self.session_variables = {}
for step in self.teststeps:
# update with config variables
step.variables.update(self.config.variables)
diff --git a/httprunner/utils.py b/httprunner/utils.py
index a7623385..a3c97651 100644
--- a/httprunner/utils.py
+++ b/httprunner/utils.py
@@ -1,15 +1,11 @@
-# encoding: utf-8
-
import collections
-import copy
-import io
-import itertools
import json
import os.path
-from typing import Union
+import platform
from loguru import logger
+from httprunner import __version__
from httprunner import exceptions
@@ -83,42 +79,6 @@ def lower_dict_keys(origin_dict):
return {key.lower(): value for key, value in origin_dict.items()}
-def deepcopy_dict(data):
- """ deepcopy dict data, ignore file object (_io.BufferedReader)
-
- Args:
- data (dict): dict data structure
- {
- 'a': 1,
- 'b': [2, 4],
- 'c': lambda x: x+1,
- 'd': open('LICENSE'),
- 'f': {
- 'f1': {'a1': 2},
- 'f2': io.open('LICENSE', 'rb'),
- }
- }
-
- Returns:
- dict: deep copied dict data, with file object unchanged.
-
- """
- try:
- return copy.deepcopy(data)
- except TypeError:
- copied_data = {}
- for key, value in data.items():
- if isinstance(value, dict):
- copied_data[key] = deepcopy_dict(value)
- else:
- try:
- copied_data[key] = copy.deepcopy(value)
- except TypeError:
- copied_data[key] = value
-
- return copied_data
-
-
def print_info(info_mapping):
""" print info in mapping.
@@ -164,47 +124,6 @@ def print_info(info_mapping):
logger.info(content)
-def gen_cartesian_product(*args):
- """ generate cartesian product for lists
-
- Args:
- args (list of list): lists to be generated with cartesian product
-
- Returns:
- list: cartesian product in list
-
- Examples:
-
- >>> arg1 = [{"a": 1}, {"a": 2}]
- >>> arg2 = [{"x": 111, "y": 112}, {"x": 121, "y": 122}]
- >>> args = [arg1, arg2]
- >>> gen_cartesian_product(*args)
- >>> # same as below
- >>> gen_cartesian_product(arg1, arg2)
- [
- {'a': 1, 'x': 111, 'y': 112},
- {'a': 1, 'x': 121, 'y': 122},
- {'a': 2, 'x': 111, 'y': 112},
- {'a': 2, 'x': 121, 'y': 122}
- ]
-
- """
- if not args:
- return []
- elif len(args) == 1:
- return args[0]
-
- product_list = []
- for product_item_tuple in itertools.product(*args):
- product_item_dict = {}
- for item in product_item_tuple:
- product_item_dict.update(item)
-
- product_list.append(product_item_dict)
-
- return product_list
-
-
def omit_long_data(body, omit_len=512):
""" omit too long str/bytes
"""
@@ -224,64 +143,11 @@ def omit_long_data(body, omit_len=512):
return omitted_body + appendix_str
-def dump_json_file(json_data: Union[dict, list], json_file_abs_path: str) -> None:
- """ dump json data to file
- """
-
- class PythonObjectEncoder(json.JSONEncoder):
- def default(self, obj):
- try:
- return super().default(self, obj)
- except TypeError:
- return str(obj)
-
- file_foder_path = os.path.dirname(json_file_abs_path)
- if not os.path.isdir(file_foder_path):
- os.makedirs(file_foder_path)
-
- try:
- with io.open(json_file_abs_path, "w", encoding="utf-8") as outfile:
- json.dump(
- json_data,
- outfile,
- indent=4,
- separators=(",", ":"),
- ensure_ascii=False,
- cls=PythonObjectEncoder,
- )
-
- msg = f"dump file: {json_file_abs_path}"
- logger.info(msg)
-
- except TypeError as ex:
- msg = f"Failed to dump json file: {json_file_abs_path}\nReason: {ex}"
- logger.error(msg)
-
-
-def prepare_log_file_abs_path(test_path: str, file_name: str) -> str:
- """ prepare dump json file absolute path.
- """
- current_working_dir = os.getcwd()
-
- if not test_path:
- # running passed in testcase/testsuite data structure
- dump_file_name = f"tests_mapping.{file_name}"
- dumped_json_file_abs_path = os.path.join(
- current_working_dir, "logs", dump_file_name
- )
- return dumped_json_file_abs_path
-
- # both test_path and pwd_dir_path are absolute path
- logs_dir_path = os.path.join(current_working_dir, "logs")
-
- if os.path.isdir(test_path):
- file_foder_path = os.path.join(logs_dir_path, test_path)
- dump_file_name = f"all.{file_name}"
- else:
- file_relative_folder_path, test_file = os.path.split(test_path)
- file_foder_path = os.path.join(logs_dir_path, file_relative_folder_path)
- test_file_name, _file_suffix = os.path.splitext(test_file)
- dump_file_name = f"{test_file_name}.{file_name}"
-
- dumped_json_file_abs_path = os.path.join(file_foder_path, dump_file_name)
- return dumped_json_file_abs_path
+def get_platform():
+ return {
+ "httprunner_version": __version__,
+ "python_version": "{} {}".format(
+ platform.python_implementation(), platform.python_version()
+ ),
+ "platform": platform.platform(),
+ }
diff --git a/httprunner/utils_test.py b/httprunner/utils_test.py
index f5d33be0..252b33f0 100644
--- a/httprunner/utils_test.py
+++ b/httprunner/utils_test.py
@@ -86,75 +86,6 @@ class TestUtils(unittest.TestCase):
new_request_dict = utils.lower_dict_keys(request_dict)
self.assertEqual(None, request_dict)
- def test_deepcopy_dict(self):
- license_path = os.path.join(
- os.path.dirname(os.path.dirname(__file__)), "LICENSE"
- )
- data = {
- "a": 1,
- "b": [2, 4],
- "c": lambda x: x + 1,
- "d": open(license_path),
- "f": {"f1": {"a1": 2}, "f2": io.open(license_path, "rb"),},
- }
- new_data = utils.deepcopy_dict(data)
- data["a"] = 0
- self.assertEqual(new_data["a"], 1)
- data["f"]["f1"] = 123
- self.assertEqual(new_data["f"]["f1"], {"a1": 2})
- self.assertNotEqual(id(new_data["b"]), id(data["b"]))
- self.assertEqual(id(new_data["c"]), id(data["c"]))
- # self.assertEqual(id(new_data["d"]), id(data["d"]))
-
- def test_cartesian_product_one(self):
- parameters_content_list = [[{"a": 1}, {"a": 2}]]
- product_list = utils.gen_cartesian_product(*parameters_content_list)
- self.assertEqual(product_list, [{"a": 1}, {"a": 2}])
-
- def test_cartesian_product_multiple(self):
- parameters_content_list = [
- [{"a": 1}, {"a": 2}],
- [{"x": 111, "y": 112}, {"x": 121, "y": 122}],
- ]
- product_list = utils.gen_cartesian_product(*parameters_content_list)
- self.assertEqual(
- product_list,
- [
- {"a": 1, "x": 111, "y": 112},
- {"a": 1, "x": 121, "y": 122},
- {"a": 2, "x": 111, "y": 112},
- {"a": 2, "x": 121, "y": 122},
- ],
- )
-
- def test_cartesian_product_empty(self):
- parameters_content_list = []
- product_list = utils.gen_cartesian_product(*parameters_content_list)
- self.assertEqual(product_list, [])
-
def test_print_info(self):
info_mapping = {"a": 1, "t": (1, 2), "b": {"b1": 123}, "c": None, "d": [4, 5]}
utils.print_info(info_mapping)
-
- def test_prepare_dump_json_file_path_for_folder(self):
- # hrun tests/httpbin/a.b.c/ --save-tests
- test_path = os.path.join("tests", "httpbin", "a.b.c")
- self.assertEqual(
- utils.prepare_log_file_abs_path(test_path, "loaded.json"),
- os.path.join(os.getcwd(), "logs", "tests/httpbin/a.b.c/all.loaded.json"),
- )
-
- def test_prepare_dump_json_file_path_for_file(self):
- # hrun tests/httpbin/a.b.c/rpc.yml --save-tests
- test_path = os.path.join("tests", "httpbin", "a.b.c", "rpc.yml")
- self.assertEqual(
- utils.prepare_log_file_abs_path(test_path, "loaded.json"),
- os.path.join(os.getcwd(), "logs", "tests/httpbin/a.b.c/rpc.loaded.json"),
- )
-
- def test_prepare_dump_json_file_path_for_passed_testcase(self):
- test_path = ""
- self.assertEqual(
- utils.prepare_log_file_abs_path(test_path, "loaded.json"),
- os.path.join(os.getcwd(), "logs", "tests_mapping.loaded.json"),
- )