diff --git a/examples/demo-with-py-plugin/debugtalk.py b/examples/demo-with-py-plugin/debugtalk.py index 743e7f62..d30e6e63 100644 --- a/examples/demo-with-py-plugin/debugtalk.py +++ b/examples/demo-with-py-plugin/debugtalk.py @@ -59,7 +59,7 @@ def teardown_hook_example(name): return f"teardown_hook_example: {name}" -if __name__ == '__main__': +if __name__ == "__main__": funppy.register("get_httprunner_version", get_httprunner_version) funppy.register("sum", sum) funppy.register("sum_ints", sum_ints) diff --git a/examples/httpbin/debugtalk.py b/examples/httpbin/debugtalk.py index ef78aee0..f949e84b 100644 --- a/examples/httpbin/debugtalk.py +++ b/examples/httpbin/debugtalk.py @@ -36,8 +36,8 @@ def sum_two(m, n): def sum_status_code(status_code, expect_sum): - """ sum status code digits - e.g. 400 => 4, 201 => 3 + """sum status code digits + e.g. 400 => 4, 201 => 3 """ sum_value = 0 for digit in str(status_code): @@ -54,8 +54,7 @@ os.environ["TEST_ENV"] = "PRODUCTION" def skip_test_in_production_env(): - """ skip this test in production environment - """ + """skip this test in production environment""" return os.environ["TEST_ENV"] == "PRODUCTION" @@ -97,8 +96,7 @@ def setup_hook_remove_kwargs(request): def teardown_hook_sleep_N_secs(response, n_secs): - """ sleep n seconds after request - """ + """sleep n seconds after request""" if response.status_code == 200: time.sleep(0.1) else: diff --git a/examples/postman_echo/conftest.py b/examples/postman_echo/conftest.py index 88a30859..c894700f 100644 --- a/examples/postman_echo/conftest.py +++ b/examples/postman_echo/conftest.py @@ -54,8 +54,7 @@ def session_fixture(request): summary["details"].append(testcase_summary_json) summary_path = os.path.join( - os.getcwd(), - "examples/postman_echo/logs/request_methods/hardcode.summary.json" + os.getcwd(), "examples/postman_echo/logs/request_methods/hardcode.summary.json" ) summary_dir = os.path.dirname(summary_path) os.makedirs(summary_dir, exist_ok=True) diff --git a/examples/postman_echo/request_methods/demo_testsuite.yml b/examples/postman_echo/request_methods/demo_testsuite.yml deleted file mode 100644 index 4c367a6b..00000000 --- a/examples/postman_echo/request_methods/demo_testsuite.yml +++ /dev/null @@ -1,22 +0,0 @@ -config: - name: "demo testsuite" - variables: ${get_testsuite_config_variables()} - -testcases: -- - name: request with functions - testcase: request_methods/request_with_functions.yml - weight: 2 - variables: - foo1: testcase_ref_bar11 - expect_foo1: testcase_ref_bar11 - expect_foo2: testsuite_config_bar2 -- - name: request with referenced testcase - testcase: request_methods/request_with_testcase_reference.yml - weight: 3 - variables: - foo1: testcase_ref_bar12 - expect_foo1: testcase_ref_bar12 - foo2: testcase_ref_bar22 - expect_foo2: testcase_ref_bar22 diff --git a/hrp/internal/scaffold/templates/plugin/debugtalk.py b/hrp/internal/scaffold/templates/plugin/debugtalk.py index 743e7f62..d30e6e63 100644 --- a/hrp/internal/scaffold/templates/plugin/debugtalk.py +++ b/hrp/internal/scaffold/templates/plugin/debugtalk.py @@ -59,7 +59,7 @@ def teardown_hook_example(name): return f"teardown_hook_example: {name}" -if __name__ == '__main__': +if __name__ == "__main__": funppy.register("get_httprunner_version", get_httprunner_version) funppy.register("sum", sum) funppy.register("sum_ints", sum_ints) diff --git a/httprunner/builtin/functions.py b/httprunner/builtin/functions.py index 2a7c68ca..00f964fd 100644 --- a/httprunner/builtin/functions.py +++ b/httprunner/builtin/functions.py @@ -11,16 +11,14 @@ from httprunner.exceptions import ParamsError def gen_random_string(str_len): - """ generate random string with specified length - """ + """generate random string with specified length""" return "".join( random.choice(string.ascii_letters + string.digits) for _ in range(str_len) ) def get_timestamp(str_len=13): - """ get timestamp string, length can only between 0 and 16 - """ + """get timestamp string, length can only between 0 and 16""" if isinstance(str_len, int) and 0 < str_len < 17: return str(time.time()).replace(".", "")[:str_len] @@ -28,12 +26,10 @@ def get_timestamp(str_len=13): def get_current_date(fmt="%Y-%m-%d"): - """ get current date, default format is %Y-%m-%d - """ + """get current date, default format is %Y-%m-%d""" return datetime.datetime.now().strftime(fmt) def sleep(n_secs): - """ sleep n seconds - """ + """sleep n seconds""" time.sleep(n_secs) diff --git a/httprunner/cli.py b/httprunner/cli.py index c803dd0f..b26dc5be 100644 --- a/httprunner/cli.py +++ b/httprunner/cli.py @@ -55,8 +55,7 @@ def main_run(extra_args) -> enum.IntEnum: def main(): - """ API test: parse command line options and run commands. - """ + """API test: parse command line options and run commands.""" init_logger() parser = argparse.ArgumentParser(description=__description__) @@ -111,8 +110,8 @@ def main(): def main_hrun_alias(): - """ command alias - hrun = httprunner run + """command alias + hrun = httprunner run """ if len(sys.argv) == 2: if sys.argv[1] in ["-V", "--version"]: @@ -131,8 +130,8 @@ def main_hrun_alias(): def main_make_alias(): - """ command alias - hmake = httprunner make + """command alias + hmake = httprunner make """ sys.argv.insert(1, "make") main() diff --git a/httprunner/client.py b/httprunner/client.py index 8ac38f0e..8e45425c 100644 --- a/httprunner/client.py +++ b/httprunner/client.py @@ -27,8 +27,7 @@ class ApiResponse(Response): def get_req_resp_record(resp_obj: Response) -> ReqRespData: - """ get request and response info from Response() object. - """ + """get request and response info from Response() object.""" def log_print(req_or_resp, r_type): msg = f"\n================== {r_type} details ==================\n" diff --git a/httprunner/client_test.py b/httprunner/client_test.py index 2ea5a6b1..467d4246 100644 --- a/httprunner/client_test.py +++ b/httprunner/client_test.py @@ -27,7 +27,8 @@ class TestHttpSession(unittest.TestCase): self.session.request( "get", "http://httpbin.org/redirect-to?url=https%3A%2F%2Fgithub.com", - allow_redirects=True) + allow_redirects=True, + ) address = self.session.data.address self.assertNotEqual(address.server_ip, "N/A") self.assertEqual(address.server_port, 443) @@ -38,7 +39,8 @@ class TestHttpSession(unittest.TestCase): self.session.request( "get", "https://httpbin.org/redirect-to?url=https%3A%2F%2Fgithub.com", - allow_redirects=True) + allow_redirects=True, + ) address = self.session.data.address self.assertNotEqual(address.server_ip, "N/A") self.assertEqual(address.server_port, 443) @@ -49,7 +51,8 @@ class TestHttpSession(unittest.TestCase): self.session.request( "get", "http://httpbin.org/redirect-to?url=https%3A%2F%2Fgithub.com", - allow_redirects=False) + allow_redirects=False, + ) address = self.session.data.address self.assertEqual(address.server_ip, "N/A") self.assertEqual(address.server_port, 0) @@ -60,7 +63,8 @@ class TestHttpSession(unittest.TestCase): self.session.request( "get", "https://httpbin.org/redirect-to?url=https%3A%2F%2Fgithub.com", - allow_redirects=False) + allow_redirects=False, + ) address = self.session.data.address self.assertEqual(address.server_ip, "N/A") self.assertEqual(address.server_port, 0) diff --git a/httprunner/compat.py b/httprunner/compat.py index 457f1686..c7352f6a 100644 --- a/httprunner/compat.py +++ b/httprunner/compat.py @@ -14,25 +14,12 @@ from httprunner.utils import sort_dict_by_custom_order def convert_variables( - raw_variables: Union[Dict, List, Text], test_path: Text + raw_variables: Union[Dict, Text], test_path: Text ) -> Dict[Text, Any]: if isinstance(raw_variables, Dict): return raw_variables - if isinstance(raw_variables, List): - # [{"var1": 1}, {"var2": 2}] - variables: Dict[Text, Any] = {} - for var_item in raw_variables: - if not isinstance(var_item, Dict) or len(var_item) != 1: - raise exceptions.TestCaseFormatError( - f"Invalid variables format: {raw_variables}" - ) - - variables.update(var_item) - - return variables - elif isinstance(raw_variables, Text): # get variables by function, e.g. ${get_variables()} project_meta = load_project_meta(test_path) @@ -79,7 +66,7 @@ def _convert_jmespath(raw: Text) -> Text: def _convert_extractors(extractors: Union[List, Dict]) -> Dict: - """ convert extract list(v2) to dict(v3) + """convert extract list(v2) to dict(v3) Args: extractors: [{"varA": "content.varA"}, {"varB": "json.varB"}] @@ -251,8 +238,7 @@ def ensure_testcase_v3(test_content: Dict) -> Dict: def ensure_cli_args(args: List) -> List: - """ ensure compatibility with deprecated cli args in v2 - """ + """ensure compatibility with deprecated cli args in v2""" # remove deprecated --failfast if "--failfast" in args: logger.warning("remove deprecated argument: --failfast") @@ -386,8 +372,7 @@ def session_fixture(request): def ensure_path_sep(path: Text) -> Text: - """ ensure compatibility with different path separators of Linux and Windows - """ + """ensure compatibility with different path separators of Linux and Windows""" if "/" in path: path = os.sep.join(path.split("/")) diff --git a/httprunner/compat_test.py b/httprunner/compat_test.py index 661d4ad8..391133a1 100644 --- a/httprunner/compat_test.py +++ b/httprunner/compat_test.py @@ -9,11 +9,6 @@ class TestCompat(unittest.TestCase): loader.project_meta = None def test_convert_variables(self): - raw_variables = [{"var1": 1}, {"var2": "val2"}] - self.assertEqual( - compat.convert_variables(raw_variables, "examples/data/a-b.c/1.yml"), - {"var1": 1, "var2": "val2"}, - ) raw_variables = {"var1": 1, "var2": "val2"} self.assertEqual( compat.convert_variables(raw_variables, "examples/data/a-b.c/1.yml"), @@ -38,7 +33,7 @@ class TestCompat(unittest.TestCase): compat._convert_jmespath("headers.Content-Type"), 'headers."Content-Type"' ) self.assertEqual( - compat._convert_jmespath('headers.User-Agent'), 'headers."User-Agent"' + compat._convert_jmespath("headers.User-Agent"), 'headers."User-Agent"' ) self.assertEqual( compat._convert_jmespath('headers."Content-Type"'), 'headers."Content-Type"' diff --git a/httprunner/config.py b/httprunner/config.py index 0f372975..ea594b5f 100644 --- a/httprunner/config.py +++ b/httprunner/config.py @@ -5,7 +5,6 @@ from httprunner.models import TConfig, TConfigThrift class ConfigThrift(object): - def __init__(self, config: TConfig) -> None: self.__config = config self.__config.thrift = TConfigThrift() @@ -31,13 +30,9 @@ class ConfigThrift(object): class Config(object): - def __init__(self, name: Text) -> None: caller_frame = inspect.stack()[1] - self.__config = TConfig( - name=name, - path=caller_frame.filename - ) + self.__config = TConfig(name=name, path=caller_frame.filename) @property def name(self) -> Text: diff --git a/httprunner/exceptions.py b/httprunner/exceptions.py index 6559fd87..495922d7 100644 --- a/httprunner/exceptions.py +++ b/httprunner/exceptions.py @@ -85,5 +85,4 @@ class TestcaseNotFound(NotFoundError): class SummaryEmpty(MyBaseError): - """ test result summary data is empty - """ + """test result summary data is empty""" diff --git a/httprunner/ext/uploader/__init__.py b/httprunner/ext/uploader/__init__.py index 95aad33a..c284f7b3 100644 --- a/httprunner/ext/uploader/__init__.py +++ b/httprunner/ext/uploader/__init__.py @@ -76,7 +76,7 @@ def ensure_upload_ready(): def prepare_upload_step(step: TStep, functions: FunctionsMapping): - """ preprocess for upload test + """preprocess for upload test replace `upload` info with MultipartEncoder Args: @@ -102,9 +102,7 @@ def prepare_upload_step(step: TStep, functions: FunctionsMapping): return # parse upload info - step.request.upload = parse_data( - step.request.upload, step.variables, functions - ) + step.request.upload = parse_data(step.request.upload, step.variables, functions) ensure_upload_ready() params_list = [] @@ -124,7 +122,7 @@ def prepare_upload_step(step: TStep, functions: FunctionsMapping): def multipart_encoder(**kwargs): - """ initialize MultipartEncoder with uploading fields. + """initialize MultipartEncoder with uploading fields. Returns: MultipartEncoder: initialized MultipartEncoder object @@ -169,7 +167,7 @@ def multipart_encoder(**kwargs): def multipart_content_type(m_encoder) -> Text: - """ prepare Content-Type for request headers + """prepare Content-Type for request headers Args: m_encoder: MultipartEncoder object diff --git a/httprunner/loader_test.py b/httprunner/loader_test.py index 95449f43..7b09d87b 100644 --- a/httprunner/loader_test.py +++ b/httprunner/loader_test.py @@ -97,7 +97,11 @@ class TestLoader(unittest.TestCase): ) def test_load_env_path_not_exist(self): - dot_env_path = os.path.join(os.getcwd(), "tests", "data",) + dot_env_path = os.path.join( + os.getcwd(), + "tests", + "data", + ) env_variables_mapping = loader.load_dot_env_file(dot_env_path) self.assertEqual(env_variables_mapping, {}) diff --git a/httprunner/make.py b/httprunner/make.py index 962bb30a..c2b3d3b2 100644 --- a/httprunner/make.py +++ b/httprunner/make.py @@ -78,10 +78,10 @@ if __name__ == "__main__": def __ensure_absolute(path: Text) -> Text: if path.startswith("./"): # Linux/Darwin, hrun ./test.yml - path = path[len("./") :] + path = path[2:] elif path.startswith(".\\"): # Windows, hrun .\\test.yml - path = path[len(".\\") :] + path = path[3:] path = ensure_path_sep(path) project_meta = load_project_meta(path) @@ -479,8 +479,7 @@ def __make(tests_path: Text): if "config" not in test_content: logger.warning( - f"Invalid testcase file: {test_file}\n" - f"reason: missing config part." + f"Invalid testcase file: {test_file}\nreason: missing config part." ) continue elif not isinstance(test_content["config"], Dict): diff --git a/httprunner/parser.py b/httprunner/parser.py index b575cd42..e1adb7be 100644 --- a/httprunner/parser.py +++ b/httprunner/parser.py @@ -20,7 +20,7 @@ function_regex_compile = re.compile(r"\$\{([a-zA-Z_]\w*)\(([\$\w\.\-/\s=,]*)\)\} def parse_string_value(str_value: Text) -> Any: - """ parse string to number if possible + """parse string to number if possible e.g. "123" => 123 "12.2" => 12.3 "abc" => "abc" @@ -36,7 +36,7 @@ def parse_string_value(str_value: Text) -> Any: def build_url(base_url, step_url): - """ prepend url with base_url unless it's already an absolute URL """ + """prepend url with base_url unless it's already an absolute URL""" o_step_url = urlparse(step_url) if o_step_url.netloc != "": # step url is absolute url @@ -49,14 +49,16 @@ def build_url(base_url, step_url): raise exceptions.ParamsError("base url missed!") path = o_base_url.path.rstrip("/") + "/" + o_step_url.path.lstrip("/") - o_step_url = o_step_url._replace(scheme=o_base_url.scheme) \ - ._replace(netloc=o_base_url.netloc) \ + o_step_url = ( + o_step_url._replace(scheme=o_base_url.scheme) + ._replace(netloc=o_base_url.netloc) ._replace(path=path) + ) return o_step_url.geturl() def regex_findall_variables(raw_string: Text) -> List[Text]: - """ extract all variable names from content, which is in format $variable + """extract all variable names from content, which is in format $variable Args: raw_string (str): string content @@ -115,7 +117,7 @@ def regex_findall_variables(raw_string: Text) -> List[Text]: def regex_findall_functions(content: Text) -> List[Text]: - """ extract all functions from string content, which are in format ${fun()} + """extract all functions from string content, which are in format ${fun()} Args: content (str): string content @@ -148,8 +150,7 @@ def regex_findall_functions(content: Text) -> List[Text]: def extract_variables(content: Any) -> Set: - """ extract all variables in content recursively. - """ + """extract all variables in content recursively.""" if isinstance(content, (list, set, tuple)): variables = set() for item in content: @@ -169,7 +170,7 @@ def extract_variables(content: Any) -> Set: def parse_function_params(params: Text) -> Dict: - """ parse function params to args and kwargs. + """parse function params to args and kwargs. Args: params (str): function param in string @@ -220,7 +221,7 @@ def parse_function_params(params: Text) -> Dict: def get_mapping_variable( variable_name: Text, variables_mapping: VariablesMapping ) -> Any: - """ get variable from variables_mapping. + """get variable from variables_mapping. Args: variable_name (str): variable name @@ -245,7 +246,7 @@ def get_mapping_variable( def get_mapping_function( function_name: Text, functions_mapping: FunctionsMapping ) -> Callable: - """ get function from functions_mapping, + """get function from functions_mapping, if not found, then try to check if builtin function. Args: @@ -295,7 +296,7 @@ def parse_string( variables_mapping: VariablesMapping, functions_mapping: FunctionsMapping, ) -> Any: - """ parse string content with variables and functions mapping. + """parse string content with variables and functions mapping. Args: raw_string: raw string content to be parsed. @@ -402,8 +403,8 @@ def parse_data( variables_mapping: VariablesMapping = None, functions_mapping: FunctionsMapping = None, ) -> Any: - """ parse raw data with evaluated variables mapping. - Notice: variables_mapping should not contain any variable or function. + """parse raw data with evaluated variables mapping. + Notice: variables_mapping should not contain any variable or function. """ if isinstance(raw_data, str): # content in string format may contains variables and functions @@ -475,8 +476,10 @@ def parse_variables_mapping( return parsed_variables -def parse_parameters(parameters: Dict,) -> List[Dict]: - """ parse parameters and generate cartesian product. +def parse_parameters( + parameters: Dict, +) -> List[Dict]: + """parse parameters and generate cartesian product. Args: parameters (Dict) parameters: parameter name and value mapping @@ -583,17 +586,20 @@ def parse_parameters(parameters: Dict,) -> List[Dict]: class Parser(object): - def __init__(self, functions_mapping: FunctionsMapping = None) -> None: self.functions_mapping = functions_mapping - def parse_string(self, raw_string: Text, variables_mapping: VariablesMapping) -> Any: + def parse_string( + self, raw_string: Text, variables_mapping: VariablesMapping + ) -> Any: return parse_string(raw_string, variables_mapping, self.functions_mapping) def parse_variables(self, variables_mapping: VariablesMapping) -> VariablesMapping: return parse_variables_mapping(variables_mapping, self.functions_mapping) - def parse_data(self, raw_data: Any, variables_mapping: VariablesMapping = None) -> Any: + def parse_data( + self, raw_data: Any, variables_mapping: VariablesMapping = None + ) -> Any: return parse_data(raw_data, variables_mapping, self.functions_mapping) def get_mapping_function(self, func_name: Text) -> Callable: diff --git a/httprunner/parser_test.py b/httprunner/parser_test.py index eaed0f78..2ac75422 100644 --- a/httprunner/parser_test.py +++ b/httprunner/parser_test.py @@ -8,7 +8,6 @@ from httprunner.loader import load_project_meta class TestParserBasic(unittest.TestCase): - def test_build_url(self): url = parser.build_url("https://postman-echo.com", "/get") self.assertEqual(url, "https://postman-echo.com/get") diff --git a/httprunner/response.py b/httprunner/response.py index 7ee2006f..bac5632b 100644 --- a/httprunner/response.py +++ b/httprunner/response.py @@ -12,8 +12,7 @@ from httprunner.parser import Parser, parse_string_value def get_uniform_comparator(comparator: Text): - """ convert comparator alias to uniform name - """ + """convert comparator alias to uniform name""" if comparator in ["eq", "equals", "equal"]: return "equal" elif comparator in ["lt", "less_than"]: @@ -52,7 +51,7 @@ def get_uniform_comparator(comparator: Text): def uniform_validator(validator): - """ unify validator + """unify validator Args: validator (dict): validator maybe in two formats: @@ -116,7 +115,7 @@ def uniform_validator(validator): class ResponseObject(object): def __init__(self, resp_obj: requests.Response, parser: Parser): - """ initialize with a requests.Response object + """initialize with a requests.Response object Args: resp_obj (instance): requests.Response instance @@ -168,20 +167,19 @@ class ResponseObject(object): return check_value - def extract(self, - extractors: Dict[Text, Text], - variables_mapping: VariablesMapping = None, - ) -> Dict[Text, Any]: + def extract( + self, + extractors: Dict[Text, Text], + variables_mapping: VariablesMapping = None, + ) -> Dict[Text, Any]: if not extractors: return {} extract_mapping = {} for key, field in extractors.items(): - if '$' in field: + if "$" in field: # field contains variable or function - field = self.parser.parse_data( - field, variables_mapping - ) + field = self.parser.parse_data(field, variables_mapping) field_value = self._search_jmespath(field) extract_mapping[key] = field_value @@ -214,9 +212,7 @@ class ResponseObject(object): check_item = u_validator["check"] if "$" in check_item: # check_item is variable or function - check_item = self.parser.parse_data( - check_item, variables_mapping - ) + check_item = self.parser.parse_data(check_item, variables_mapping) check_item = parse_string_value(check_item) if check_item and isinstance(check_item, Text): diff --git a/httprunner/response_test.py b/httprunner/response_test.py index 8b292f46..7ab7a6fb 100644 --- a/httprunner/response_test.py +++ b/httprunner/response_test.py @@ -19,16 +19,13 @@ class TestResponse(unittest.TestCase): ] }, ) - parser = Parser(functions_mapping={ - 'get_name': lambda: 'name', - "get_num": lambda x: x - }) + parser = Parser( + functions_mapping={"get_name": lambda: "name", "get_num": lambda x: x} + ) self.resp_obj = ResponseObject(resp, parser) def test_extract(self): - variables_mapping = { - 'body': 'body' - } + variables_mapping = {"body": "body"} extract_mapping = self.resp_obj.extract( { "var_1": "body.json.locations[0]", @@ -64,6 +61,9 @@ class TestResponse(unittest.TestCase): def test_validate_functions(self): variables_mapping = {"index": 1} self.resp_obj.validate( - [{"eq": ["${get_num(0)}", 0]}, {"eq": ["${get_num($index)}", 1]},], + [ + {"eq": ["${get_num(0)}", 0]}, + {"eq": ["${get_num($index)}", 1]}, + ], variables_mapping=variables_mapping, ) diff --git a/httprunner/runner.py b/httprunner/runner.py index fe535098..af69ec61 100644 --- a/httprunner/runner.py +++ b/httprunner/runner.py @@ -17,8 +17,15 @@ from httprunner.client import HttpSession from httprunner.config import Config from httprunner.exceptions import ParamsError, ValidationFailure from httprunner.loader import load_project_meta -from httprunner.models import (ProjectMeta, StepResult, TConfig, TestCaseInOut, - TestCaseSummary, TestCaseTime, VariablesMapping) +from httprunner.models import ( + ProjectMeta, + StepResult, + TConfig, + TestCaseInOut, + TestCaseSummary, + TestCaseTime, + VariablesMapping, +) from httprunner.parser import Parser from httprunner.utils import LOGGER_FORMAT, init_logger, merge_variables @@ -55,9 +62,7 @@ class SessionRunner(object): ) self.case_id = self.case_id or str(uuid.uuid4()) self.root_dir = self.root_dir or self.__project_meta.RootDir - self.__log_path = os.path.join( - self.root_dir, "logs", f"{self.case_id}.run.log" - ) + self.__log_path = os.path.join(self.root_dir, "logs", f"{self.case_id}.run.log") self.__step_results.clear() self.session = self.session or HttpSession() @@ -87,9 +92,7 @@ class SessionRunner(object): self.__config.variables.update(self.__session_variables) if param: self.__config.variables.update(param) - self.__config.variables = self.parser.parse_variables( - self.__config.variables - ) + self.__config.variables = self.parser.parse_variables(self.__config.variables) # parse config name self.__config.name = self.parser.parse_data( @@ -176,10 +179,12 @@ class SessionRunner(object): raise else: logger.warning( - f"run step {step.name()} validation failed,wait {step.retry_interval} sec and try again") + f"run step {step.name()} validation failed,wait {step.retry_interval} sec and try again" + ) time.sleep(step.retry_interval) logger.info( - f"run step retry ({i+1}/{step.retry_times} time): {step.name()} >>>>>>") + f"run step retry ({i+1}/{step.retry_times} time): {step.name()} >>>>>>" + ) # save extracted variables to session variables self.__session_variables.update(step_result.export_vars) diff --git a/httprunner/step.py b/httprunner/step.py index c349de3e..a5221f16 100644 --- a/httprunner/step.py +++ b/httprunner/step.py @@ -2,12 +2,15 @@ from typing import Union from httprunner.models import StepResult, TRequest, TStep, TestCase from httprunner.runner import HttpRunner -from httprunner.step_request import RequestWithOptionalArgs, StepRequestExtraction, StepRequestValidation +from httprunner.step_request import ( + RequestWithOptionalArgs, + StepRequestExtraction, + StepRequestValidation, +) from httprunner.step_testcase import StepRefCase class Step(object): - def __init__( self, step: Union[ diff --git a/httprunner/step_request.py b/httprunner/step_request.py index a681e448..9299cba2 100644 --- a/httprunner/step_request.py +++ b/httprunner/step_request.py @@ -6,15 +6,24 @@ from loguru import logger from httprunner import utils from httprunner.exceptions import ValidationFailure from httprunner.ext.uploader import prepare_upload_step -from httprunner.models import (Hooks, IStep, MethodEnum, StepResult, TRequest, - TStep, VariablesMapping) +from httprunner.models import ( + Hooks, + IStep, + MethodEnum, + StepResult, + TRequest, + TStep, + VariablesMapping, +) from httprunner.parser import build_url from httprunner.response import ResponseObject from httprunner.runner import HttpRunner -def call_hooks(runner: HttpRunner, hooks: Hooks, step_variables: VariablesMapping, hook_msg: Text): - """ call hook actions. +def call_hooks( + runner: HttpRunner, hooks: Hooks, step_variables: VariablesMapping, hook_msg: Text +): + """call hook actions. Args: hooks (list): each hook in hooks list maybe in two format. @@ -46,9 +55,7 @@ def call_hooks(runner: HttpRunner, hooks: Hooks, step_variables: VariablesMappin elif isinstance(hook, Dict) and len(hook) == 1: # format 2: {"var": "${func()}"} var_name, hook_content = list(hook.items())[0] - hook_content_eval = runner.parser.parse_data( - hook_content, step_variables - ) + hook_content_eval = runner.parser.parse_data(hook_content, step_variables) logger.debug( f"call hook function: {hook_content}, got value: {hook_content_eval}" ) @@ -73,9 +80,7 @@ def run_step_request(runner: HttpRunner, step: TStep) -> StepResult: prepare_upload_step(step, functions) request_dict = step.request.dict() request_dict.pop("upload", None) - parsed_request_dict = runner.parser.parse_data( - request_dict, step.variables - ) + parsed_request_dict = runner.parser.parse_data(request_dict, step.variables) parsed_request_dict["headers"].setdefault( "HRUN-Request-ID", f"HRUN-{runner.case_id}-{str(int(time.time() * 1000))[-6:]}", @@ -136,9 +141,7 @@ def run_step_request(runner: HttpRunner, step: TStep) -> StepResult: # validate validators = step.validators try: - resp_obj.validate( - validators, variables_mapping - ) + resp_obj.validate(validators, variables_mapping) step_result.success = True except ValidationFailure: log_req_resp_details() @@ -162,9 +165,7 @@ class StepRequestValidation(IStep): def assert_equal( self, jmes_path: Text, expected_value: Any, message: Text = "" ) -> "StepRequestValidation": - self.__step.validators.append( - {"equal": [jmes_path, expected_value, message]} - ) + self.__step.validators.append({"equal": [jmes_path, expected_value, message]}) return self def assert_not_equal( @@ -418,7 +419,6 @@ class RequestWithOptionalArgs(IStep): class RunRequest(object): - def __init__(self, name: Text): self.__step = TStep(name=name) diff --git a/httprunner/step_request_test.py b/httprunner/step_request_test.py index 7a54e494..58164acc 100644 --- a/httprunner/step_request_test.py +++ b/httprunner/step_request_test.py @@ -1,10 +1,11 @@ import unittest -from examples.postman_echo.request_methods.request_with_functions_test import TestCaseRequestWithFunctions +from examples.postman_echo.request_methods.request_with_functions_test import ( + TestCaseRequestWithFunctions, +) class TestRunRequest(unittest.TestCase): - def test_run_request(self): runner = TestCaseRequestWithFunctions().test_start() summary = runner.get_summary() diff --git a/httprunner/step_testcase.py b/httprunner/step_testcase.py index b61285c5..c011168f 100644 --- a/httprunner/step_testcase.py +++ b/httprunner/step_testcase.py @@ -22,11 +22,9 @@ def run_step_testcase(runner: HttpRunner, step: TStep) -> StepResult: # step.testcase is a referenced testcase, e.g. RequestWithFunctions ref_case_runner = step.testcase() - ref_case_runner.with_session(runner.session) \ - .with_case_id(runner.case_id) \ - .with_variables(step_variables) \ - .with_export(step_export) \ - .test_start() + ref_case_runner.with_session(runner.session).with_case_id( + runner.case_id + ).with_variables(step_variables).with_export(step_export).test_start() # teardown hooks if step.teardown_hooks: diff --git a/httprunner/step_testcase_test.py b/httprunner/step_testcase_test.py index 27a7301c..9f64e0c0 100644 --- a/httprunner/step_testcase_test.py +++ b/httprunner/step_testcase_test.py @@ -2,19 +2,22 @@ import unittest from httprunner.runner import HttpRunner from httprunner.step_testcase import RunTestCase -from examples.postman_echo.request_methods.request_with_functions_test import TestCaseRequestWithFunctions +from examples.postman_echo.request_methods.request_with_functions_test import ( + TestCaseRequestWithFunctions, +) class TestRunTestCase(unittest.TestCase): - def setUp(self): self.runner = HttpRunner() def test_run_testcase_by_path(self): - step_result = RunTestCase("run referenced testcase").call( - TestCaseRequestWithFunctions - ).run(self.runner) + step_result = ( + RunTestCase("run referenced testcase") + .call(TestCaseRequestWithFunctions) + .run(self.runner) + ) self.assertTrue(step_result.success) self.assertEqual(step_result.name, "run referenced testcase") self.assertEqual(len(step_result.data), 3) diff --git a/httprunner/utils.py b/httprunner/utils.py index 9c0306f6..3a42f3b3 100644 --- a/httprunner/utils.py +++ b/httprunner/utils.py @@ -32,18 +32,20 @@ def init_sentry_sdk(): class GAClient(object): - version = '1' # GA API Version - report_url = 'https://www.google-analytics.com/collect' - report_debug_url = 'https://www.google-analytics.com/debug/collect' # used for debug + version = "1" # GA API Version + report_url = "https://www.google-analytics.com/collect" + report_debug_url = ( + "https://www.google-analytics.com/debug/collect" # used for debug + ) def __init__(self, tracking_id: Text): self.http_client = requests.Session() self.label = f"v{__version__}" self.common_params = { - 'v': self.version, - 'tid': tracking_id, # Tracking ID / Property ID, XX-XXXXXXX-X - 'cid': uuid.getnode(), # Anonymous Client ID - 'ua': f'HttpRunner/{__version__}', + "v": self.version, + "tid": tracking_id, # Tracking ID / Property ID, XX-XXXXXXX-X + "cid": uuid.getnode(), # Anonymous Client ID + "ua": f"HttpRunner/{__version__}", } # do not send GA events in CI environment self.__is_ci = os.getenv("DISABLE_GA") == "true" @@ -53,16 +55,16 @@ class GAClient(object): return data = { - 't': 'event', # Event hit type = event - 'ec': category, # Required. Event Category. - 'ea': action, # Required. Event Action. - 'el': self.label, # Optional. Event label, used as version. - 'ev': value, # Optional. Event value, must be non-negative integer + "t": "event", # Event hit type = event + "ec": category, # Required. Event Category. + "ea": action, # Required. Event Action. + "el": self.label, # Optional. Event label, used as version. + "ev": value, # Optional. Event value, must be non-negative integer } data.update(self.common_params) try: self.http_client.post(self.report_url, data=data, timeout=5) - except Exception: # ProxyError, SSLError, ConnectionError + except Exception: # ProxyError, SSLError, ConnectionError pass def track_user_timing(self, category: Text, variable: Text, duration: int): @@ -70,16 +72,16 @@ class GAClient(object): return data = { - 't': 'timing', # Event hit type = timing - 'utc': category, # Required. user timing category. e.g. jsonLoader - 'utv': variable, # Required. timing variable. e.g. load - 'utt': duration, # Required. time took duration. - 'utl': self.label, # Optional. user timing label, used as version. + "t": "timing", # Event hit type = timing + "utc": category, # Required. user timing category. e.g. jsonLoader + "utv": variable, # Required. timing variable. e.g. load + "utt": duration, # Required. time took duration. + "utl": self.label, # Optional. user timing label, used as version. } data.update(self.common_params) try: self.http_client.post(self.report_url, data=data, timeout=5) - except Exception: # ProxyError, SSLError, ConnectionError + except Exception: # ProxyError, SSLError, ConnectionError pass @@ -87,23 +89,21 @@ ga_client = GAClient("UA-114587036-1") def set_os_environ(variables_mapping): - """ set variables mapping to os.environ - """ + """set variables mapping to os.environ""" for variable in variables_mapping: os.environ[variable] = variables_mapping[variable] logger.debug(f"Set OS environment variable: {variable}") def unset_os_environ(variables_mapping): - """ unset variables mapping to os.environ - """ + """unset variables mapping to os.environ""" for variable in variables_mapping: os.environ.pop(variable) logger.debug(f"Unset OS environment variable: {variable}") def get_os_environ(variable_name): - """ get value of environment variable. + """get value of environment variable. Args: variable_name(str): variable name @@ -122,7 +122,7 @@ def get_os_environ(variable_name): def lower_dict_keys(origin_dict): - """ convert keys in dict to lower case + """convert keys in dict to lower case Args: origin_dict (dict): mapping data structure @@ -157,7 +157,7 @@ def lower_dict_keys(origin_dict): def print_info(info_mapping): - """ print info in mapping. + """print info in mapping. Args: info_mapping (dict): input(variables) or output mapping. @@ -202,8 +202,7 @@ def print_info(info_mapping): def omit_long_data(body, omit_len=512): - """ omit too long str/bytes - """ + """omit too long str/bytes""" if not isinstance(body, (str, bytes)): return body @@ -244,8 +243,7 @@ def sort_dict_by_custom_order(raw_dict: Dict, custom_order: List): class ExtendJSONEncoder(json.JSONEncoder): - """ especially used to safely dump json data with python object, such as MultipartEncoder - """ + """especially used to safely dump json data with python object, such as MultipartEncoder""" def default(self, obj): try: @@ -257,8 +255,7 @@ class ExtendJSONEncoder(json.JSONEncoder): def merge_variables( variables: VariablesMapping, variables_to_be_overridden: VariablesMapping ) -> VariablesMapping: - """ merge two variables mapping, the first variables have higher priority - """ + """merge two variables mapping, the first variables have higher priority""" step_new_variables = {} for key, value in variables.items(): if f"${key}" == value or "${" + key + "}" == value: @@ -283,7 +280,7 @@ def is_support_multiprocessing() -> bool: def gen_cartesian_product(*args: List[Dict]) -> List[Dict]: - """ generate cartesian product for lists + """generate cartesian product for lists Args: args (list of list): lists to be generated with cartesian product