refactor: reformat all code with black

This commit is contained in:
debugtalk
2022-04-23 15:05:21 +08:00
parent 7eaa8a4ac0
commit 418a16fcee
27 changed files with 169 additions and 213 deletions

View File

@@ -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)

View File

@@ -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:

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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()

View File

@@ -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"

View File

@@ -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)

View File

@@ -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("/"))

View File

@@ -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"'

View File

@@ -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:

View File

@@ -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"""
"""

View File

@@ -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

View File

@@ -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, {})

View File

@@ -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):

View File

@@ -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:

View File

@@ -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")

View File

@@ -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):

View File

@@ -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,
) )

View File

@@ -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)

View File

@@ -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[

View File

@@ -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)

View File

@@ -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()

View File

@@ -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:

View File

@@ -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)

View File

@@ -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