mirror of
https://github.com/httprunner/httprunner.git
synced 2026-07-12 16:01:27 +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}"
|
return f"teardown_hook_example: {name}"
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
funppy.register("get_httprunner_version", get_httprunner_version)
|
funppy.register("get_httprunner_version", get_httprunner_version)
|
||||||
funppy.register("sum", sum)
|
funppy.register("sum", sum)
|
||||||
funppy.register("sum_ints", sum_ints)
|
funppy.register("sum_ints", sum_ints)
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ def sum_two(m, n):
|
|||||||
|
|
||||||
|
|
||||||
def sum_status_code(status_code, expect_sum):
|
def sum_status_code(status_code, expect_sum):
|
||||||
""" sum status code digits
|
"""sum status code digits
|
||||||
e.g. 400 => 4, 201 => 3
|
e.g. 400 => 4, 201 => 3
|
||||||
"""
|
"""
|
||||||
sum_value = 0
|
sum_value = 0
|
||||||
for digit in str(status_code):
|
for digit in str(status_code):
|
||||||
@@ -54,8 +54,7 @@ os.environ["TEST_ENV"] = "PRODUCTION"
|
|||||||
|
|
||||||
|
|
||||||
def skip_test_in_production_env():
|
def skip_test_in_production_env():
|
||||||
""" skip this test in production environment
|
"""skip this test in production environment"""
|
||||||
"""
|
|
||||||
return os.environ["TEST_ENV"] == "PRODUCTION"
|
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):
|
def teardown_hook_sleep_N_secs(response, n_secs):
|
||||||
""" sleep n seconds after request
|
"""sleep n seconds after request"""
|
||||||
"""
|
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -54,8 +54,7 @@ def session_fixture(request):
|
|||||||
summary["details"].append(testcase_summary_json)
|
summary["details"].append(testcase_summary_json)
|
||||||
|
|
||||||
summary_path = os.path.join(
|
summary_path = os.path.join(
|
||||||
os.getcwd(),
|
os.getcwd(), "examples/postman_echo/logs/request_methods/hardcode.summary.json"
|
||||||
"examples/postman_echo/logs/request_methods/hardcode.summary.json"
|
|
||||||
)
|
)
|
||||||
summary_dir = os.path.dirname(summary_path)
|
summary_dir = os.path.dirname(summary_path)
|
||||||
os.makedirs(summary_dir, exist_ok=True)
|
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}"
|
return f"teardown_hook_example: {name}"
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
funppy.register("get_httprunner_version", get_httprunner_version)
|
funppy.register("get_httprunner_version", get_httprunner_version)
|
||||||
funppy.register("sum", sum)
|
funppy.register("sum", sum)
|
||||||
funppy.register("sum_ints", sum_ints)
|
funppy.register("sum_ints", sum_ints)
|
||||||
|
|||||||
@@ -11,16 +11,14 @@ from httprunner.exceptions import ParamsError
|
|||||||
|
|
||||||
|
|
||||||
def gen_random_string(str_len):
|
def gen_random_string(str_len):
|
||||||
""" generate random string with specified length
|
"""generate random string with specified length"""
|
||||||
"""
|
|
||||||
return "".join(
|
return "".join(
|
||||||
random.choice(string.ascii_letters + string.digits) for _ in range(str_len)
|
random.choice(string.ascii_letters + string.digits) for _ in range(str_len)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_timestamp(str_len=13):
|
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:
|
if isinstance(str_len, int) and 0 < str_len < 17:
|
||||||
return str(time.time()).replace(".", "")[:str_len]
|
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"):
|
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)
|
return datetime.datetime.now().strftime(fmt)
|
||||||
|
|
||||||
|
|
||||||
def sleep(n_secs):
|
def sleep(n_secs):
|
||||||
""" sleep n seconds
|
"""sleep n seconds"""
|
||||||
"""
|
|
||||||
time.sleep(n_secs)
|
time.sleep(n_secs)
|
||||||
|
|||||||
@@ -55,8 +55,7 @@ def main_run(extra_args) -> enum.IntEnum:
|
|||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
""" API test: parse command line options and run commands.
|
"""API test: parse command line options and run commands."""
|
||||||
"""
|
|
||||||
init_logger()
|
init_logger()
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description=__description__)
|
parser = argparse.ArgumentParser(description=__description__)
|
||||||
@@ -111,8 +110,8 @@ def main():
|
|||||||
|
|
||||||
|
|
||||||
def main_hrun_alias():
|
def main_hrun_alias():
|
||||||
""" command alias
|
"""command alias
|
||||||
hrun = httprunner run
|
hrun = httprunner run
|
||||||
"""
|
"""
|
||||||
if len(sys.argv) == 2:
|
if len(sys.argv) == 2:
|
||||||
if sys.argv[1] in ["-V", "--version"]:
|
if sys.argv[1] in ["-V", "--version"]:
|
||||||
@@ -131,8 +130,8 @@ def main_hrun_alias():
|
|||||||
|
|
||||||
|
|
||||||
def main_make_alias():
|
def main_make_alias():
|
||||||
""" command alias
|
"""command alias
|
||||||
hmake = httprunner make
|
hmake = httprunner make
|
||||||
"""
|
"""
|
||||||
sys.argv.insert(1, "make")
|
sys.argv.insert(1, "make")
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -27,8 +27,7 @@ class ApiResponse(Response):
|
|||||||
|
|
||||||
|
|
||||||
def get_req_resp_record(resp_obj: Response) -> ReqRespData:
|
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):
|
def log_print(req_or_resp, r_type):
|
||||||
msg = f"\n================== {r_type} details ==================\n"
|
msg = f"\n================== {r_type} details ==================\n"
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ class TestHttpSession(unittest.TestCase):
|
|||||||
self.session.request(
|
self.session.request(
|
||||||
"get",
|
"get",
|
||||||
"http://httpbin.org/redirect-to?url=https%3A%2F%2Fgithub.com",
|
"http://httpbin.org/redirect-to?url=https%3A%2F%2Fgithub.com",
|
||||||
allow_redirects=True)
|
allow_redirects=True,
|
||||||
|
)
|
||||||
address = self.session.data.address
|
address = self.session.data.address
|
||||||
self.assertNotEqual(address.server_ip, "N/A")
|
self.assertNotEqual(address.server_ip, "N/A")
|
||||||
self.assertEqual(address.server_port, 443)
|
self.assertEqual(address.server_port, 443)
|
||||||
@@ -38,7 +39,8 @@ class TestHttpSession(unittest.TestCase):
|
|||||||
self.session.request(
|
self.session.request(
|
||||||
"get",
|
"get",
|
||||||
"https://httpbin.org/redirect-to?url=https%3A%2F%2Fgithub.com",
|
"https://httpbin.org/redirect-to?url=https%3A%2F%2Fgithub.com",
|
||||||
allow_redirects=True)
|
allow_redirects=True,
|
||||||
|
)
|
||||||
address = self.session.data.address
|
address = self.session.data.address
|
||||||
self.assertNotEqual(address.server_ip, "N/A")
|
self.assertNotEqual(address.server_ip, "N/A")
|
||||||
self.assertEqual(address.server_port, 443)
|
self.assertEqual(address.server_port, 443)
|
||||||
@@ -49,7 +51,8 @@ class TestHttpSession(unittest.TestCase):
|
|||||||
self.session.request(
|
self.session.request(
|
||||||
"get",
|
"get",
|
||||||
"http://httpbin.org/redirect-to?url=https%3A%2F%2Fgithub.com",
|
"http://httpbin.org/redirect-to?url=https%3A%2F%2Fgithub.com",
|
||||||
allow_redirects=False)
|
allow_redirects=False,
|
||||||
|
)
|
||||||
address = self.session.data.address
|
address = self.session.data.address
|
||||||
self.assertEqual(address.server_ip, "N/A")
|
self.assertEqual(address.server_ip, "N/A")
|
||||||
self.assertEqual(address.server_port, 0)
|
self.assertEqual(address.server_port, 0)
|
||||||
@@ -60,7 +63,8 @@ class TestHttpSession(unittest.TestCase):
|
|||||||
self.session.request(
|
self.session.request(
|
||||||
"get",
|
"get",
|
||||||
"https://httpbin.org/redirect-to?url=https%3A%2F%2Fgithub.com",
|
"https://httpbin.org/redirect-to?url=https%3A%2F%2Fgithub.com",
|
||||||
allow_redirects=False)
|
allow_redirects=False,
|
||||||
|
)
|
||||||
address = self.session.data.address
|
address = self.session.data.address
|
||||||
self.assertEqual(address.server_ip, "N/A")
|
self.assertEqual(address.server_ip, "N/A")
|
||||||
self.assertEqual(address.server_port, 0)
|
self.assertEqual(address.server_port, 0)
|
||||||
|
|||||||
@@ -14,25 +14,12 @@ from httprunner.utils import sort_dict_by_custom_order
|
|||||||
|
|
||||||
|
|
||||||
def convert_variables(
|
def convert_variables(
|
||||||
raw_variables: Union[Dict, List, Text], test_path: Text
|
raw_variables: Union[Dict, Text], test_path: Text
|
||||||
) -> Dict[Text, Any]:
|
) -> Dict[Text, Any]:
|
||||||
|
|
||||||
if isinstance(raw_variables, Dict):
|
if isinstance(raw_variables, Dict):
|
||||||
return raw_variables
|
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):
|
elif isinstance(raw_variables, Text):
|
||||||
# get variables by function, e.g. ${get_variables()}
|
# get variables by function, e.g. ${get_variables()}
|
||||||
project_meta = load_project_meta(test_path)
|
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:
|
def _convert_extractors(extractors: Union[List, Dict]) -> Dict:
|
||||||
""" convert extract list(v2) to dict(v3)
|
"""convert extract list(v2) to dict(v3)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
extractors: [{"varA": "content.varA"}, {"varB": "json.varB"}]
|
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:
|
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
|
# remove deprecated --failfast
|
||||||
if "--failfast" in args:
|
if "--failfast" in args:
|
||||||
logger.warning("remove deprecated argument: --failfast")
|
logger.warning("remove deprecated argument: --failfast")
|
||||||
@@ -386,8 +372,7 @@ def session_fixture(request):
|
|||||||
|
|
||||||
|
|
||||||
def ensure_path_sep(path: Text) -> Text:
|
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:
|
if "/" in path:
|
||||||
path = os.sep.join(path.split("/"))
|
path = os.sep.join(path.split("/"))
|
||||||
|
|
||||||
|
|||||||
@@ -9,11 +9,6 @@ class TestCompat(unittest.TestCase):
|
|||||||
loader.project_meta = None
|
loader.project_meta = None
|
||||||
|
|
||||||
def test_convert_variables(self):
|
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"}
|
raw_variables = {"var1": 1, "var2": "val2"}
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
compat.convert_variables(raw_variables, "examples/data/a-b.c/1.yml"),
|
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"'
|
compat._convert_jmespath("headers.Content-Type"), 'headers."Content-Type"'
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
compat._convert_jmespath('headers.User-Agent'), 'headers."User-Agent"'
|
compat._convert_jmespath("headers.User-Agent"), 'headers."User-Agent"'
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
compat._convert_jmespath('headers."Content-Type"'), 'headers."Content-Type"'
|
compat._convert_jmespath('headers."Content-Type"'), 'headers."Content-Type"'
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ from httprunner.models import TConfig, TConfigThrift
|
|||||||
|
|
||||||
|
|
||||||
class ConfigThrift(object):
|
class ConfigThrift(object):
|
||||||
|
|
||||||
def __init__(self, config: TConfig) -> None:
|
def __init__(self, config: TConfig) -> None:
|
||||||
self.__config = config
|
self.__config = config
|
||||||
self.__config.thrift = TConfigThrift()
|
self.__config.thrift = TConfigThrift()
|
||||||
@@ -31,13 +30,9 @@ class ConfigThrift(object):
|
|||||||
|
|
||||||
|
|
||||||
class Config(object):
|
class Config(object):
|
||||||
|
|
||||||
def __init__(self, name: Text) -> None:
|
def __init__(self, name: Text) -> None:
|
||||||
caller_frame = inspect.stack()[1]
|
caller_frame = inspect.stack()[1]
|
||||||
self.__config = TConfig(
|
self.__config = TConfig(name=name, path=caller_frame.filename)
|
||||||
name=name,
|
|
||||||
path=caller_frame.filename
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name(self) -> Text:
|
def name(self) -> Text:
|
||||||
|
|||||||
@@ -85,5 +85,4 @@ class TestcaseNotFound(NotFoundError):
|
|||||||
|
|
||||||
|
|
||||||
class SummaryEmpty(MyBaseError):
|
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):
|
def prepare_upload_step(step: TStep, functions: FunctionsMapping):
|
||||||
""" preprocess for upload test
|
"""preprocess for upload test
|
||||||
replace `upload` info with MultipartEncoder
|
replace `upload` info with MultipartEncoder
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -102,9 +102,7 @@ def prepare_upload_step(step: TStep, functions: FunctionsMapping):
|
|||||||
return
|
return
|
||||||
|
|
||||||
# parse upload info
|
# parse upload info
|
||||||
step.request.upload = parse_data(
|
step.request.upload = parse_data(step.request.upload, step.variables, functions)
|
||||||
step.request.upload, step.variables, functions
|
|
||||||
)
|
|
||||||
|
|
||||||
ensure_upload_ready()
|
ensure_upload_ready()
|
||||||
params_list = []
|
params_list = []
|
||||||
@@ -124,7 +122,7 @@ def prepare_upload_step(step: TStep, functions: FunctionsMapping):
|
|||||||
|
|
||||||
|
|
||||||
def multipart_encoder(**kwargs):
|
def multipart_encoder(**kwargs):
|
||||||
""" initialize MultipartEncoder with uploading fields.
|
"""initialize MultipartEncoder with uploading fields.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
MultipartEncoder: initialized MultipartEncoder object
|
MultipartEncoder: initialized MultipartEncoder object
|
||||||
@@ -169,7 +167,7 @@ def multipart_encoder(**kwargs):
|
|||||||
|
|
||||||
|
|
||||||
def multipart_content_type(m_encoder) -> Text:
|
def multipart_content_type(m_encoder) -> Text:
|
||||||
""" prepare Content-Type for request headers
|
"""prepare Content-Type for request headers
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
m_encoder: MultipartEncoder object
|
m_encoder: MultipartEncoder object
|
||||||
|
|||||||
@@ -97,7 +97,11 @@ class TestLoader(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def test_load_env_path_not_exist(self):
|
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)
|
env_variables_mapping = loader.load_dot_env_file(dot_env_path)
|
||||||
self.assertEqual(env_variables_mapping, {})
|
self.assertEqual(env_variables_mapping, {})
|
||||||
|
|
||||||
|
|||||||
@@ -78,10 +78,10 @@ if __name__ == "__main__":
|
|||||||
def __ensure_absolute(path: Text) -> Text:
|
def __ensure_absolute(path: Text) -> Text:
|
||||||
if path.startswith("./"):
|
if path.startswith("./"):
|
||||||
# Linux/Darwin, hrun ./test.yml
|
# Linux/Darwin, hrun ./test.yml
|
||||||
path = path[len("./") :]
|
path = path[2:]
|
||||||
elif path.startswith(".\\"):
|
elif path.startswith(".\\"):
|
||||||
# Windows, hrun .\\test.yml
|
# Windows, hrun .\\test.yml
|
||||||
path = path[len(".\\") :]
|
path = path[3:]
|
||||||
|
|
||||||
path = ensure_path_sep(path)
|
path = ensure_path_sep(path)
|
||||||
project_meta = load_project_meta(path)
|
project_meta = load_project_meta(path)
|
||||||
@@ -479,8 +479,7 @@ def __make(tests_path: Text):
|
|||||||
|
|
||||||
if "config" not in test_content:
|
if "config" not in test_content:
|
||||||
logger.warning(
|
logger.warning(
|
||||||
f"Invalid testcase file: {test_file}\n"
|
f"Invalid testcase file: {test_file}\nreason: missing config part."
|
||||||
f"reason: missing config part."
|
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
elif not isinstance(test_content["config"], Dict):
|
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:
|
def parse_string_value(str_value: Text) -> Any:
|
||||||
""" parse string to number if possible
|
"""parse string to number if possible
|
||||||
e.g. "123" => 123
|
e.g. "123" => 123
|
||||||
"12.2" => 12.3
|
"12.2" => 12.3
|
||||||
"abc" => "abc"
|
"abc" => "abc"
|
||||||
@@ -36,7 +36,7 @@ def parse_string_value(str_value: Text) -> Any:
|
|||||||
|
|
||||||
|
|
||||||
def build_url(base_url, step_url):
|
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)
|
o_step_url = urlparse(step_url)
|
||||||
if o_step_url.netloc != "":
|
if o_step_url.netloc != "":
|
||||||
# step url is absolute url
|
# step url is absolute url
|
||||||
@@ -49,14 +49,16 @@ def build_url(base_url, step_url):
|
|||||||
raise exceptions.ParamsError("base url missed!")
|
raise exceptions.ParamsError("base url missed!")
|
||||||
|
|
||||||
path = o_base_url.path.rstrip("/") + "/" + o_step_url.path.lstrip("/")
|
path = o_base_url.path.rstrip("/") + "/" + o_step_url.path.lstrip("/")
|
||||||
o_step_url = o_step_url._replace(scheme=o_base_url.scheme) \
|
o_step_url = (
|
||||||
._replace(netloc=o_base_url.netloc) \
|
o_step_url._replace(scheme=o_base_url.scheme)
|
||||||
|
._replace(netloc=o_base_url.netloc)
|
||||||
._replace(path=path)
|
._replace(path=path)
|
||||||
|
)
|
||||||
return o_step_url.geturl()
|
return o_step_url.geturl()
|
||||||
|
|
||||||
|
|
||||||
def regex_findall_variables(raw_string: Text) -> List[Text]:
|
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:
|
Args:
|
||||||
raw_string (str): string content
|
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]:
|
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:
|
Args:
|
||||||
content (str): string content
|
content (str): string content
|
||||||
@@ -148,8 +150,7 @@ def regex_findall_functions(content: Text) -> List[Text]:
|
|||||||
|
|
||||||
|
|
||||||
def extract_variables(content: Any) -> Set:
|
def extract_variables(content: Any) -> Set:
|
||||||
""" extract all variables in content recursively.
|
"""extract all variables in content recursively."""
|
||||||
"""
|
|
||||||
if isinstance(content, (list, set, tuple)):
|
if isinstance(content, (list, set, tuple)):
|
||||||
variables = set()
|
variables = set()
|
||||||
for item in content:
|
for item in content:
|
||||||
@@ -169,7 +170,7 @@ def extract_variables(content: Any) -> Set:
|
|||||||
|
|
||||||
|
|
||||||
def parse_function_params(params: Text) -> Dict:
|
def parse_function_params(params: Text) -> Dict:
|
||||||
""" parse function params to args and kwargs.
|
"""parse function params to args and kwargs.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
params (str): function param in string
|
params (str): function param in string
|
||||||
@@ -220,7 +221,7 @@ def parse_function_params(params: Text) -> Dict:
|
|||||||
def get_mapping_variable(
|
def get_mapping_variable(
|
||||||
variable_name: Text, variables_mapping: VariablesMapping
|
variable_name: Text, variables_mapping: VariablesMapping
|
||||||
) -> Any:
|
) -> Any:
|
||||||
""" get variable from variables_mapping.
|
"""get variable from variables_mapping.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
variable_name (str): variable name
|
variable_name (str): variable name
|
||||||
@@ -245,7 +246,7 @@ def get_mapping_variable(
|
|||||||
def get_mapping_function(
|
def get_mapping_function(
|
||||||
function_name: Text, functions_mapping: FunctionsMapping
|
function_name: Text, functions_mapping: FunctionsMapping
|
||||||
) -> Callable:
|
) -> Callable:
|
||||||
""" get function from functions_mapping,
|
"""get function from functions_mapping,
|
||||||
if not found, then try to check if builtin function.
|
if not found, then try to check if builtin function.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -295,7 +296,7 @@ def parse_string(
|
|||||||
variables_mapping: VariablesMapping,
|
variables_mapping: VariablesMapping,
|
||||||
functions_mapping: FunctionsMapping,
|
functions_mapping: FunctionsMapping,
|
||||||
) -> Any:
|
) -> Any:
|
||||||
""" parse string content with variables and functions mapping.
|
"""parse string content with variables and functions mapping.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
raw_string: raw string content to be parsed.
|
raw_string: raw string content to be parsed.
|
||||||
@@ -402,8 +403,8 @@ def parse_data(
|
|||||||
variables_mapping: VariablesMapping = None,
|
variables_mapping: VariablesMapping = None,
|
||||||
functions_mapping: FunctionsMapping = None,
|
functions_mapping: FunctionsMapping = None,
|
||||||
) -> Any:
|
) -> Any:
|
||||||
""" parse raw data with evaluated variables mapping.
|
"""parse raw data with evaluated variables mapping.
|
||||||
Notice: variables_mapping should not contain any variable or function.
|
Notice: variables_mapping should not contain any variable or function.
|
||||||
"""
|
"""
|
||||||
if isinstance(raw_data, str):
|
if isinstance(raw_data, str):
|
||||||
# content in string format may contains variables and functions
|
# content in string format may contains variables and functions
|
||||||
@@ -475,8 +476,10 @@ def parse_variables_mapping(
|
|||||||
return parsed_variables
|
return parsed_variables
|
||||||
|
|
||||||
|
|
||||||
def parse_parameters(parameters: Dict,) -> List[Dict]:
|
def parse_parameters(
|
||||||
""" parse parameters and generate cartesian product.
|
parameters: Dict,
|
||||||
|
) -> List[Dict]:
|
||||||
|
"""parse parameters and generate cartesian product.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
parameters (Dict) parameters: parameter name and value mapping
|
parameters (Dict) parameters: parameter name and value mapping
|
||||||
@@ -583,17 +586,20 @@ def parse_parameters(parameters: Dict,) -> List[Dict]:
|
|||||||
|
|
||||||
|
|
||||||
class Parser(object):
|
class Parser(object):
|
||||||
|
|
||||||
def __init__(self, functions_mapping: FunctionsMapping = None) -> None:
|
def __init__(self, functions_mapping: FunctionsMapping = None) -> None:
|
||||||
self.functions_mapping = functions_mapping
|
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)
|
return parse_string(raw_string, variables_mapping, self.functions_mapping)
|
||||||
|
|
||||||
def parse_variables(self, variables_mapping: VariablesMapping) -> VariablesMapping:
|
def parse_variables(self, variables_mapping: VariablesMapping) -> VariablesMapping:
|
||||||
return parse_variables_mapping(variables_mapping, self.functions_mapping)
|
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)
|
return parse_data(raw_data, variables_mapping, self.functions_mapping)
|
||||||
|
|
||||||
def get_mapping_function(self, func_name: Text) -> Callable:
|
def get_mapping_function(self, func_name: Text) -> Callable:
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ from httprunner.loader import load_project_meta
|
|||||||
|
|
||||||
|
|
||||||
class TestParserBasic(unittest.TestCase):
|
class TestParserBasic(unittest.TestCase):
|
||||||
|
|
||||||
def test_build_url(self):
|
def test_build_url(self):
|
||||||
url = parser.build_url("https://postman-echo.com", "/get")
|
url = parser.build_url("https://postman-echo.com", "/get")
|
||||||
self.assertEqual(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):
|
def get_uniform_comparator(comparator: Text):
|
||||||
""" convert comparator alias to uniform name
|
"""convert comparator alias to uniform name"""
|
||||||
"""
|
|
||||||
if comparator in ["eq", "equals", "equal"]:
|
if comparator in ["eq", "equals", "equal"]:
|
||||||
return "equal"
|
return "equal"
|
||||||
elif comparator in ["lt", "less_than"]:
|
elif comparator in ["lt", "less_than"]:
|
||||||
@@ -52,7 +51,7 @@ def get_uniform_comparator(comparator: Text):
|
|||||||
|
|
||||||
|
|
||||||
def uniform_validator(validator):
|
def uniform_validator(validator):
|
||||||
""" unify validator
|
"""unify validator
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
validator (dict): validator maybe in two formats:
|
validator (dict): validator maybe in two formats:
|
||||||
@@ -116,7 +115,7 @@ def uniform_validator(validator):
|
|||||||
|
|
||||||
class ResponseObject(object):
|
class ResponseObject(object):
|
||||||
def __init__(self, resp_obj: requests.Response, parser: Parser):
|
def __init__(self, resp_obj: requests.Response, parser: Parser):
|
||||||
""" initialize with a requests.Response object
|
"""initialize with a requests.Response object
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
resp_obj (instance): requests.Response instance
|
resp_obj (instance): requests.Response instance
|
||||||
@@ -168,20 +167,19 @@ class ResponseObject(object):
|
|||||||
|
|
||||||
return check_value
|
return check_value
|
||||||
|
|
||||||
def extract(self,
|
def extract(
|
||||||
extractors: Dict[Text, Text],
|
self,
|
||||||
variables_mapping: VariablesMapping = None,
|
extractors: Dict[Text, Text],
|
||||||
) -> Dict[Text, Any]:
|
variables_mapping: VariablesMapping = None,
|
||||||
|
) -> Dict[Text, Any]:
|
||||||
if not extractors:
|
if not extractors:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
extract_mapping = {}
|
extract_mapping = {}
|
||||||
for key, field in extractors.items():
|
for key, field in extractors.items():
|
||||||
if '$' in field:
|
if "$" in field:
|
||||||
# field contains variable or function
|
# field contains variable or function
|
||||||
field = self.parser.parse_data(
|
field = self.parser.parse_data(field, variables_mapping)
|
||||||
field, variables_mapping
|
|
||||||
)
|
|
||||||
field_value = self._search_jmespath(field)
|
field_value = self._search_jmespath(field)
|
||||||
extract_mapping[key] = field_value
|
extract_mapping[key] = field_value
|
||||||
|
|
||||||
@@ -214,9 +212,7 @@ class ResponseObject(object):
|
|||||||
check_item = u_validator["check"]
|
check_item = u_validator["check"]
|
||||||
if "$" in check_item:
|
if "$" in check_item:
|
||||||
# check_item is variable or function
|
# check_item is variable or function
|
||||||
check_item = self.parser.parse_data(
|
check_item = self.parser.parse_data(check_item, variables_mapping)
|
||||||
check_item, variables_mapping
|
|
||||||
)
|
|
||||||
check_item = parse_string_value(check_item)
|
check_item = parse_string_value(check_item)
|
||||||
|
|
||||||
if check_item and isinstance(check_item, Text):
|
if check_item and isinstance(check_item, Text):
|
||||||
|
|||||||
@@ -19,16 +19,13 @@ class TestResponse(unittest.TestCase):
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
parser = Parser(functions_mapping={
|
parser = Parser(
|
||||||
'get_name': lambda: 'name',
|
functions_mapping={"get_name": lambda: "name", "get_num": lambda x: x}
|
||||||
"get_num": lambda x: x
|
)
|
||||||
})
|
|
||||||
self.resp_obj = ResponseObject(resp, parser)
|
self.resp_obj = ResponseObject(resp, parser)
|
||||||
|
|
||||||
def test_extract(self):
|
def test_extract(self):
|
||||||
variables_mapping = {
|
variables_mapping = {"body": "body"}
|
||||||
'body': 'body'
|
|
||||||
}
|
|
||||||
extract_mapping = self.resp_obj.extract(
|
extract_mapping = self.resp_obj.extract(
|
||||||
{
|
{
|
||||||
"var_1": "body.json.locations[0]",
|
"var_1": "body.json.locations[0]",
|
||||||
@@ -64,6 +61,9 @@ class TestResponse(unittest.TestCase):
|
|||||||
def test_validate_functions(self):
|
def test_validate_functions(self):
|
||||||
variables_mapping = {"index": 1}
|
variables_mapping = {"index": 1}
|
||||||
self.resp_obj.validate(
|
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,
|
variables_mapping=variables_mapping,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -17,8 +17,15 @@ from httprunner.client import HttpSession
|
|||||||
from httprunner.config import Config
|
from httprunner.config import Config
|
||||||
from httprunner.exceptions import ParamsError, ValidationFailure
|
from httprunner.exceptions import ParamsError, ValidationFailure
|
||||||
from httprunner.loader import load_project_meta
|
from httprunner.loader import load_project_meta
|
||||||
from httprunner.models import (ProjectMeta, StepResult, TConfig, TestCaseInOut,
|
from httprunner.models import (
|
||||||
TestCaseSummary, TestCaseTime, VariablesMapping)
|
ProjectMeta,
|
||||||
|
StepResult,
|
||||||
|
TConfig,
|
||||||
|
TestCaseInOut,
|
||||||
|
TestCaseSummary,
|
||||||
|
TestCaseTime,
|
||||||
|
VariablesMapping,
|
||||||
|
)
|
||||||
from httprunner.parser import Parser
|
from httprunner.parser import Parser
|
||||||
from httprunner.utils import LOGGER_FORMAT, init_logger, merge_variables
|
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.case_id = self.case_id or str(uuid.uuid4())
|
||||||
self.root_dir = self.root_dir or self.__project_meta.RootDir
|
self.root_dir = self.root_dir or self.__project_meta.RootDir
|
||||||
self.__log_path = os.path.join(
|
self.__log_path = os.path.join(self.root_dir, "logs", f"{self.case_id}.run.log")
|
||||||
self.root_dir, "logs", f"{self.case_id}.run.log"
|
|
||||||
)
|
|
||||||
|
|
||||||
self.__step_results.clear()
|
self.__step_results.clear()
|
||||||
self.session = self.session or HttpSession()
|
self.session = self.session or HttpSession()
|
||||||
@@ -87,9 +92,7 @@ class SessionRunner(object):
|
|||||||
self.__config.variables.update(self.__session_variables)
|
self.__config.variables.update(self.__session_variables)
|
||||||
if param:
|
if param:
|
||||||
self.__config.variables.update(param)
|
self.__config.variables.update(param)
|
||||||
self.__config.variables = self.parser.parse_variables(
|
self.__config.variables = self.parser.parse_variables(self.__config.variables)
|
||||||
self.__config.variables
|
|
||||||
)
|
|
||||||
|
|
||||||
# parse config name
|
# parse config name
|
||||||
self.__config.name = self.parser.parse_data(
|
self.__config.name = self.parser.parse_data(
|
||||||
@@ -176,10 +179,12 @@ class SessionRunner(object):
|
|||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
logger.warning(
|
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)
|
time.sleep(step.retry_interval)
|
||||||
logger.info(
|
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
|
# save extracted variables to session variables
|
||||||
self.__session_variables.update(step_result.export_vars)
|
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.models import StepResult, TRequest, TStep, TestCase
|
||||||
from httprunner.runner import HttpRunner
|
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
|
from httprunner.step_testcase import StepRefCase
|
||||||
|
|
||||||
|
|
||||||
class Step(object):
|
class Step(object):
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
step: Union[
|
step: Union[
|
||||||
|
|||||||
@@ -6,15 +6,24 @@ from loguru import logger
|
|||||||
from httprunner import utils
|
from httprunner import utils
|
||||||
from httprunner.exceptions import ValidationFailure
|
from httprunner.exceptions import ValidationFailure
|
||||||
from httprunner.ext.uploader import prepare_upload_step
|
from httprunner.ext.uploader import prepare_upload_step
|
||||||
from httprunner.models import (Hooks, IStep, MethodEnum, StepResult, TRequest,
|
from httprunner.models import (
|
||||||
TStep, VariablesMapping)
|
Hooks,
|
||||||
|
IStep,
|
||||||
|
MethodEnum,
|
||||||
|
StepResult,
|
||||||
|
TRequest,
|
||||||
|
TStep,
|
||||||
|
VariablesMapping,
|
||||||
|
)
|
||||||
from httprunner.parser import build_url
|
from httprunner.parser import build_url
|
||||||
from httprunner.response import ResponseObject
|
from httprunner.response import ResponseObject
|
||||||
from httprunner.runner import HttpRunner
|
from httprunner.runner import HttpRunner
|
||||||
|
|
||||||
|
|
||||||
def call_hooks(runner: HttpRunner, hooks: Hooks, step_variables: VariablesMapping, hook_msg: Text):
|
def call_hooks(
|
||||||
""" call hook actions.
|
runner: HttpRunner, hooks: Hooks, step_variables: VariablesMapping, hook_msg: Text
|
||||||
|
):
|
||||||
|
"""call hook actions.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
hooks (list): each hook in hooks list maybe in two format.
|
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:
|
elif isinstance(hook, Dict) and len(hook) == 1:
|
||||||
# format 2: {"var": "${func()}"}
|
# format 2: {"var": "${func()}"}
|
||||||
var_name, hook_content = list(hook.items())[0]
|
var_name, hook_content = list(hook.items())[0]
|
||||||
hook_content_eval = runner.parser.parse_data(
|
hook_content_eval = runner.parser.parse_data(hook_content, step_variables)
|
||||||
hook_content, step_variables
|
|
||||||
)
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
f"call hook function: {hook_content}, got value: {hook_content_eval}"
|
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)
|
prepare_upload_step(step, functions)
|
||||||
request_dict = step.request.dict()
|
request_dict = step.request.dict()
|
||||||
request_dict.pop("upload", None)
|
request_dict.pop("upload", None)
|
||||||
parsed_request_dict = runner.parser.parse_data(
|
parsed_request_dict = runner.parser.parse_data(request_dict, step.variables)
|
||||||
request_dict, step.variables
|
|
||||||
)
|
|
||||||
parsed_request_dict["headers"].setdefault(
|
parsed_request_dict["headers"].setdefault(
|
||||||
"HRUN-Request-ID",
|
"HRUN-Request-ID",
|
||||||
f"HRUN-{runner.case_id}-{str(int(time.time() * 1000))[-6:]}",
|
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
|
# validate
|
||||||
validators = step.validators
|
validators = step.validators
|
||||||
try:
|
try:
|
||||||
resp_obj.validate(
|
resp_obj.validate(validators, variables_mapping)
|
||||||
validators, variables_mapping
|
|
||||||
)
|
|
||||||
step_result.success = True
|
step_result.success = True
|
||||||
except ValidationFailure:
|
except ValidationFailure:
|
||||||
log_req_resp_details()
|
log_req_resp_details()
|
||||||
@@ -162,9 +165,7 @@ class StepRequestValidation(IStep):
|
|||||||
def assert_equal(
|
def assert_equal(
|
||||||
self, jmes_path: Text, expected_value: Any, message: Text = ""
|
self, jmes_path: Text, expected_value: Any, message: Text = ""
|
||||||
) -> "StepRequestValidation":
|
) -> "StepRequestValidation":
|
||||||
self.__step.validators.append(
|
self.__step.validators.append({"equal": [jmes_path, expected_value, message]})
|
||||||
{"equal": [jmes_path, expected_value, message]}
|
|
||||||
)
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def assert_not_equal(
|
def assert_not_equal(
|
||||||
@@ -418,7 +419,6 @@ class RequestWithOptionalArgs(IStep):
|
|||||||
|
|
||||||
|
|
||||||
class RunRequest(object):
|
class RunRequest(object):
|
||||||
|
|
||||||
def __init__(self, name: Text):
|
def __init__(self, name: Text):
|
||||||
self.__step = TStep(name=name)
|
self.__step = TStep(name=name)
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import unittest
|
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):
|
class TestRunRequest(unittest.TestCase):
|
||||||
|
|
||||||
def test_run_request(self):
|
def test_run_request(self):
|
||||||
runner = TestCaseRequestWithFunctions().test_start()
|
runner = TestCaseRequestWithFunctions().test_start()
|
||||||
summary = runner.get_summary()
|
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
|
# step.testcase is a referenced testcase, e.g. RequestWithFunctions
|
||||||
ref_case_runner = step.testcase()
|
ref_case_runner = step.testcase()
|
||||||
ref_case_runner.with_session(runner.session) \
|
ref_case_runner.with_session(runner.session).with_case_id(
|
||||||
.with_case_id(runner.case_id) \
|
runner.case_id
|
||||||
.with_variables(step_variables) \
|
).with_variables(step_variables).with_export(step_export).test_start()
|
||||||
.with_export(step_export) \
|
|
||||||
.test_start()
|
|
||||||
|
|
||||||
# teardown hooks
|
# teardown hooks
|
||||||
if step.teardown_hooks:
|
if step.teardown_hooks:
|
||||||
|
|||||||
@@ -2,19 +2,22 @@ import unittest
|
|||||||
|
|
||||||
from httprunner.runner import HttpRunner
|
from httprunner.runner import HttpRunner
|
||||||
from httprunner.step_testcase import RunTestCase
|
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):
|
class TestRunTestCase(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.runner = HttpRunner()
|
self.runner = HttpRunner()
|
||||||
|
|
||||||
def test_run_testcase_by_path(self):
|
def test_run_testcase_by_path(self):
|
||||||
|
|
||||||
step_result = RunTestCase("run referenced testcase").call(
|
step_result = (
|
||||||
TestCaseRequestWithFunctions
|
RunTestCase("run referenced testcase")
|
||||||
).run(self.runner)
|
.call(TestCaseRequestWithFunctions)
|
||||||
|
.run(self.runner)
|
||||||
|
)
|
||||||
self.assertTrue(step_result.success)
|
self.assertTrue(step_result.success)
|
||||||
self.assertEqual(step_result.name, "run referenced testcase")
|
self.assertEqual(step_result.name, "run referenced testcase")
|
||||||
self.assertEqual(len(step_result.data), 3)
|
self.assertEqual(len(step_result.data), 3)
|
||||||
|
|||||||
@@ -32,18 +32,20 @@ def init_sentry_sdk():
|
|||||||
|
|
||||||
class GAClient(object):
|
class GAClient(object):
|
||||||
|
|
||||||
version = '1' # GA API Version
|
version = "1" # GA API Version
|
||||||
report_url = 'https://www.google-analytics.com/collect'
|
report_url = "https://www.google-analytics.com/collect"
|
||||||
report_debug_url = 'https://www.google-analytics.com/debug/collect' # used for debug
|
report_debug_url = (
|
||||||
|
"https://www.google-analytics.com/debug/collect" # used for debug
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self, tracking_id: Text):
|
def __init__(self, tracking_id: Text):
|
||||||
self.http_client = requests.Session()
|
self.http_client = requests.Session()
|
||||||
self.label = f"v{__version__}"
|
self.label = f"v{__version__}"
|
||||||
self.common_params = {
|
self.common_params = {
|
||||||
'v': self.version,
|
"v": self.version,
|
||||||
'tid': tracking_id, # Tracking ID / Property ID, XX-XXXXXXX-X
|
"tid": tracking_id, # Tracking ID / Property ID, XX-XXXXXXX-X
|
||||||
'cid': uuid.getnode(), # Anonymous Client ID
|
"cid": uuid.getnode(), # Anonymous Client ID
|
||||||
'ua': f'HttpRunner/{__version__}',
|
"ua": f"HttpRunner/{__version__}",
|
||||||
}
|
}
|
||||||
# do not send GA events in CI environment
|
# do not send GA events in CI environment
|
||||||
self.__is_ci = os.getenv("DISABLE_GA") == "true"
|
self.__is_ci = os.getenv("DISABLE_GA") == "true"
|
||||||
@@ -53,16 +55,16 @@ class GAClient(object):
|
|||||||
return
|
return
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
't': 'event', # Event hit type = event
|
"t": "event", # Event hit type = event
|
||||||
'ec': category, # Required. Event Category.
|
"ec": category, # Required. Event Category.
|
||||||
'ea': action, # Required. Event Action.
|
"ea": action, # Required. Event Action.
|
||||||
'el': self.label, # Optional. Event label, used as version.
|
"el": self.label, # Optional. Event label, used as version.
|
||||||
'ev': value, # Optional. Event value, must be non-negative integer
|
"ev": value, # Optional. Event value, must be non-negative integer
|
||||||
}
|
}
|
||||||
data.update(self.common_params)
|
data.update(self.common_params)
|
||||||
try:
|
try:
|
||||||
self.http_client.post(self.report_url, data=data, timeout=5)
|
self.http_client.post(self.report_url, data=data, timeout=5)
|
||||||
except Exception: # ProxyError, SSLError, ConnectionError
|
except Exception: # ProxyError, SSLError, ConnectionError
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def track_user_timing(self, category: Text, variable: Text, duration: int):
|
def track_user_timing(self, category: Text, variable: Text, duration: int):
|
||||||
@@ -70,16 +72,16 @@ class GAClient(object):
|
|||||||
return
|
return
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
't': 'timing', # Event hit type = timing
|
"t": "timing", # Event hit type = timing
|
||||||
'utc': category, # Required. user timing category. e.g. jsonLoader
|
"utc": category, # Required. user timing category. e.g. jsonLoader
|
||||||
'utv': variable, # Required. timing variable. e.g. load
|
"utv": variable, # Required. timing variable. e.g. load
|
||||||
'utt': duration, # Required. time took duration.
|
"utt": duration, # Required. time took duration.
|
||||||
'utl': self.label, # Optional. user timing label, used as version.
|
"utl": self.label, # Optional. user timing label, used as version.
|
||||||
}
|
}
|
||||||
data.update(self.common_params)
|
data.update(self.common_params)
|
||||||
try:
|
try:
|
||||||
self.http_client.post(self.report_url, data=data, timeout=5)
|
self.http_client.post(self.report_url, data=data, timeout=5)
|
||||||
except Exception: # ProxyError, SSLError, ConnectionError
|
except Exception: # ProxyError, SSLError, ConnectionError
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@@ -87,23 +89,21 @@ ga_client = GAClient("UA-114587036-1")
|
|||||||
|
|
||||||
|
|
||||||
def set_os_environ(variables_mapping):
|
def set_os_environ(variables_mapping):
|
||||||
""" set variables mapping to os.environ
|
"""set variables mapping to os.environ"""
|
||||||
"""
|
|
||||||
for variable in variables_mapping:
|
for variable in variables_mapping:
|
||||||
os.environ[variable] = variables_mapping[variable]
|
os.environ[variable] = variables_mapping[variable]
|
||||||
logger.debug(f"Set OS environment variable: {variable}")
|
logger.debug(f"Set OS environment variable: {variable}")
|
||||||
|
|
||||||
|
|
||||||
def unset_os_environ(variables_mapping):
|
def unset_os_environ(variables_mapping):
|
||||||
""" unset variables mapping to os.environ
|
"""unset variables mapping to os.environ"""
|
||||||
"""
|
|
||||||
for variable in variables_mapping:
|
for variable in variables_mapping:
|
||||||
os.environ.pop(variable)
|
os.environ.pop(variable)
|
||||||
logger.debug(f"Unset OS environment variable: {variable}")
|
logger.debug(f"Unset OS environment variable: {variable}")
|
||||||
|
|
||||||
|
|
||||||
def get_os_environ(variable_name):
|
def get_os_environ(variable_name):
|
||||||
""" get value of environment variable.
|
"""get value of environment variable.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
variable_name(str): variable name
|
variable_name(str): variable name
|
||||||
@@ -122,7 +122,7 @@ def get_os_environ(variable_name):
|
|||||||
|
|
||||||
|
|
||||||
def lower_dict_keys(origin_dict):
|
def lower_dict_keys(origin_dict):
|
||||||
""" convert keys in dict to lower case
|
"""convert keys in dict to lower case
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
origin_dict (dict): mapping data structure
|
origin_dict (dict): mapping data structure
|
||||||
@@ -157,7 +157,7 @@ def lower_dict_keys(origin_dict):
|
|||||||
|
|
||||||
|
|
||||||
def print_info(info_mapping):
|
def print_info(info_mapping):
|
||||||
""" print info in mapping.
|
"""print info in mapping.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
info_mapping (dict): input(variables) or output mapping.
|
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):
|
def omit_long_data(body, omit_len=512):
|
||||||
""" omit too long str/bytes
|
"""omit too long str/bytes"""
|
||||||
"""
|
|
||||||
if not isinstance(body, (str, bytes)):
|
if not isinstance(body, (str, bytes)):
|
||||||
return body
|
return body
|
||||||
|
|
||||||
@@ -244,8 +243,7 @@ def sort_dict_by_custom_order(raw_dict: Dict, custom_order: List):
|
|||||||
|
|
||||||
|
|
||||||
class ExtendJSONEncoder(json.JSONEncoder):
|
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):
|
def default(self, obj):
|
||||||
try:
|
try:
|
||||||
@@ -257,8 +255,7 @@ class ExtendJSONEncoder(json.JSONEncoder):
|
|||||||
def merge_variables(
|
def merge_variables(
|
||||||
variables: VariablesMapping, variables_to_be_overridden: VariablesMapping
|
variables: VariablesMapping, variables_to_be_overridden: VariablesMapping
|
||||||
) -> 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 = {}
|
step_new_variables = {}
|
||||||
for key, value in variables.items():
|
for key, value in variables.items():
|
||||||
if f"${key}" == value or "${" + key + "}" == value:
|
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]:
|
def gen_cartesian_product(*args: List[Dict]) -> List[Dict]:
|
||||||
""" generate cartesian product for lists
|
"""generate cartesian product for lists
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
args (list of list): lists to be generated with cartesian product
|
args (list of list): lists to be generated with cartesian product
|
||||||
|
|||||||
Reference in New Issue
Block a user