mirror of
https://github.com/httprunner/httprunner.git
synced 2026-05-13 08:59:44 +08:00
refactor: reformat all code with black
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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("/"))
|
||||
|
||||
|
||||
@@ -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"'
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -85,5 +85,4 @@ class TestcaseNotFound(NotFoundError):
|
||||
|
||||
|
||||
class SummaryEmpty(MyBaseError):
|
||||
""" test result summary data is empty
|
||||
"""
|
||||
"""test result summary data is empty"""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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, {})
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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[
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user