mirror of
https://github.com/httprunner/httprunner.git
synced 2026-09-05 15:37:35 +08:00
fix bug when nested function and variable exists in variables:
e.g.
"variables": {
"host2": "https://httprunner.org",
"num4": "${sum_two($num0, 5)}",
"num3": "${sum_two($num2, 4)}",
"num2": "${sum_two($num1, 3)}",
"num1": "${sum_two(1, 2)}"
}
This commit is contained in:
+93
-60
@@ -464,7 +464,7 @@ def parse_string_functions(content, variables_mapping, functions_mapping):
|
|||||||
return content
|
return content
|
||||||
|
|
||||||
|
|
||||||
def parse_string_variables(content, variables_mapping):
|
def parse_string_variables(content, variables_mapping, functions_mapping):
|
||||||
""" parse string content with variables mapping.
|
""" parse string content with variables mapping.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@@ -477,7 +477,7 @@ def parse_string_variables(content, variables_mapping):
|
|||||||
Examples:
|
Examples:
|
||||||
>>> content = "/api/users/$uid"
|
>>> content = "/api/users/$uid"
|
||||||
>>> variables_mapping = {"$uid": 1000}
|
>>> variables_mapping = {"$uid": 1000}
|
||||||
>>> parse_string_variables(content, variables_mapping)
|
>>> parse_string_variables(content, variables_mapping, {})
|
||||||
"/api/users/1000"
|
"/api/users/1000"
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -485,30 +485,52 @@ def parse_string_variables(content, variables_mapping):
|
|||||||
for variable_name in variables_list:
|
for variable_name in variables_list:
|
||||||
variable_value = get_mapping_variable(variable_name, variables_mapping)
|
variable_value = get_mapping_variable(variable_name, variables_mapping)
|
||||||
|
|
||||||
|
if variable_name == "request" and isinstance(variable_value, dict) \
|
||||||
|
and "url" in variable_value and "method" in variable_value:
|
||||||
|
# call setup_hooks action with $request
|
||||||
|
for key, value in variable_value.items():
|
||||||
|
variable_value[key] = parse_data(
|
||||||
|
value,
|
||||||
|
variables_mapping,
|
||||||
|
functions_mapping
|
||||||
|
)
|
||||||
|
parsed_variable_value = variable_value
|
||||||
|
elif "${}".format(variable_name) == variable_value:
|
||||||
|
# variable_name = "token"
|
||||||
|
# variables_mapping = {"token": "$token"}
|
||||||
|
parsed_variable_value = variable_value
|
||||||
|
else:
|
||||||
|
parsed_variable_value = parse_data(
|
||||||
|
variable_value,
|
||||||
|
variables_mapping,
|
||||||
|
functions_mapping
|
||||||
|
)
|
||||||
|
|
||||||
# TODO: replace variable label from $var to {{var}}
|
# TODO: replace variable label from $var to {{var}}
|
||||||
if "${}".format(variable_name) == content:
|
if "${}".format(variable_name) == content:
|
||||||
# content is a variable
|
# content is a variable
|
||||||
content = variable_value
|
content = parsed_variable_value
|
||||||
else:
|
else:
|
||||||
# content contains one or several variables
|
# content contains one or several variables
|
||||||
if not isinstance(variable_value, str):
|
if not isinstance(parsed_variable_value, str):
|
||||||
variable_value = builtin_str(variable_value)
|
parsed_variable_value = builtin_str(parsed_variable_value)
|
||||||
|
|
||||||
content = content.replace(
|
content = content.replace(
|
||||||
"${}".format(variable_name),
|
"${}".format(variable_name),
|
||||||
variable_value, 1
|
parsed_variable_value, 1
|
||||||
)
|
)
|
||||||
|
|
||||||
return content
|
return content
|
||||||
|
|
||||||
|
|
||||||
def parse_data(content, variables_mapping=None, functions_mapping=None):
|
def parse_data(content, variables_mapping=None, functions_mapping=None, raise_if_variable_not_found=True):
|
||||||
""" parse content with variables mapping
|
""" parse content with variables mapping
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
content (str/dict/list/numeric/bool/type): content to be parsed
|
content (str/dict/list/numeric/bool/type): content to be parsed
|
||||||
variables_mapping (dict): variables mapping.
|
variables_mapping (dict): variables mapping.
|
||||||
functions_mapping (dict): functions mapping.
|
functions_mapping (dict): functions mapping.
|
||||||
|
raise_if_variable_not_found (bool): if set False, exception will not raise when VariableNotFound occurred.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
parsed content.
|
parsed content.
|
||||||
@@ -536,15 +558,30 @@ def parse_data(content, variables_mapping=None, functions_mapping=None):
|
|||||||
|
|
||||||
if isinstance(content, (list, set, tuple)):
|
if isinstance(content, (list, set, tuple)):
|
||||||
return [
|
return [
|
||||||
parse_data(item, variables_mapping, functions_mapping)
|
parse_data(
|
||||||
|
item,
|
||||||
|
variables_mapping,
|
||||||
|
functions_mapping,
|
||||||
|
raise_if_variable_not_found
|
||||||
|
)
|
||||||
for item in content
|
for item in content
|
||||||
]
|
]
|
||||||
|
|
||||||
if isinstance(content, dict):
|
if isinstance(content, dict):
|
||||||
parsed_content = {}
|
parsed_content = {}
|
||||||
for key, value in content.items():
|
for key, value in content.items():
|
||||||
parsed_key = parse_data(key, variables_mapping, functions_mapping)
|
parsed_key = parse_data(
|
||||||
parsed_value = parse_data(value, variables_mapping, functions_mapping)
|
key,
|
||||||
|
variables_mapping,
|
||||||
|
functions_mapping,
|
||||||
|
raise_if_variable_not_found
|
||||||
|
)
|
||||||
|
parsed_value = parse_data(
|
||||||
|
value,
|
||||||
|
variables_mapping,
|
||||||
|
functions_mapping,
|
||||||
|
raise_if_variable_not_found
|
||||||
|
)
|
||||||
parsed_content[parsed_key] = parsed_value
|
parsed_content[parsed_key] = parsed_value
|
||||||
|
|
||||||
return parsed_content
|
return parsed_content
|
||||||
@@ -555,12 +592,23 @@ def parse_data(content, variables_mapping=None, functions_mapping=None):
|
|||||||
functions_mapping = functions_mapping or {}
|
functions_mapping = functions_mapping or {}
|
||||||
content = content.strip()
|
content = content.strip()
|
||||||
|
|
||||||
# replace functions with evaluated value
|
try:
|
||||||
# Notice: _eval_content_functions must be called before _eval_content_variables
|
# replace functions with evaluated value
|
||||||
content = parse_string_functions(content, variables_mapping, functions_mapping)
|
# Notice: parse_string_functions must be called before parse_string_variables
|
||||||
|
content = parse_string_functions(
|
||||||
# replace variables with binding value
|
content,
|
||||||
content = parse_string_variables(content, variables_mapping)
|
variables_mapping,
|
||||||
|
functions_mapping
|
||||||
|
)
|
||||||
|
# replace variables with binding value
|
||||||
|
content = parse_string_variables(
|
||||||
|
content,
|
||||||
|
variables_mapping,
|
||||||
|
functions_mapping
|
||||||
|
)
|
||||||
|
except exceptions.VariableNotFound:
|
||||||
|
if raise_if_variable_not_found:
|
||||||
|
raise
|
||||||
|
|
||||||
return content
|
return content
|
||||||
|
|
||||||
@@ -707,14 +755,12 @@ def __parse_config(config, project_mapping):
|
|||||||
# parse config variables
|
# parse config variables
|
||||||
parsed_config_variables = {}
|
parsed_config_variables = {}
|
||||||
for key, value in raw_config_variables_mapping.items():
|
for key, value in raw_config_variables_mapping.items():
|
||||||
try:
|
parsed_value = parse_data(
|
||||||
parsed_value = parse_data(
|
value,
|
||||||
value,
|
raw_config_variables_mapping,
|
||||||
raw_config_variables_mapping,
|
functions,
|
||||||
functions
|
raise_if_variable_not_found=False
|
||||||
)
|
)
|
||||||
except exceptions.VariableNotFound:
|
|
||||||
pass
|
|
||||||
parsed_config_variables[key] = parsed_value
|
parsed_config_variables[key] = parsed_value
|
||||||
|
|
||||||
if parsed_config_variables:
|
if parsed_config_variables:
|
||||||
@@ -771,16 +817,20 @@ def __parse_testcase_tests(tests, config, project_mapping):
|
|||||||
test_dict.pop("variables", {}),
|
test_dict.pop("variables", {}),
|
||||||
config_variables
|
config_variables
|
||||||
)
|
)
|
||||||
|
test_dict["variables"] = parse_data(
|
||||||
|
test_dict["variables"],
|
||||||
|
test_dict["variables"],
|
||||||
|
functions,
|
||||||
|
raise_if_variable_not_found=False
|
||||||
|
)
|
||||||
|
|
||||||
# parse test_dict name
|
# parse test_dict name
|
||||||
try:
|
test_dict["name"] = parse_data(
|
||||||
test_dict["name"] = parse_data(
|
test_dict.pop("name", ""),
|
||||||
test_dict.pop("name", ""),
|
test_dict["variables"],
|
||||||
test_dict["variables"],
|
functions,
|
||||||
functions
|
raise_if_variable_not_found=False
|
||||||
)
|
)
|
||||||
except exceptions.VariableNotFound:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if "testcase_def" in test_dict:
|
if "testcase_def" in test_dict:
|
||||||
# test_dict is nested testcase
|
# test_dict is nested testcase
|
||||||
@@ -808,34 +858,17 @@ def __parse_testcase_tests(tests, config, project_mapping):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# build path with base_url
|
# build path with base_url
|
||||||
try:
|
# variable in current url maybe extracted from former api
|
||||||
request_url = parse_data(
|
request_url = parse_data(
|
||||||
test_dict["request"]["url"],
|
test_dict["request"]["url"],
|
||||||
test_dict["variables"],
|
test_dict["variables"],
|
||||||
functions
|
functions,
|
||||||
)
|
raise_if_variable_not_found=False
|
||||||
except exceptions.VariableNotFound:
|
)
|
||||||
""" variable in current url maybe extracted from former api
|
test_dict["request"]["url"] = utils.build_url(
|
||||||
"teststeps": [
|
base_url,
|
||||||
{
|
request_url
|
||||||
"request": {},
|
)
|
||||||
"extract": {
|
|
||||||
"host1": content.host1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"request": {
|
|
||||||
"url": "$host1/path1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
"""
|
|
||||||
request_url = test_dict["request"]["url"]
|
|
||||||
finally:
|
|
||||||
test_dict["request"]["url"] = utils.build_url(
|
|
||||||
base_url,
|
|
||||||
request_url
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def _parse_testcase(testcase, project_mapping):
|
def _parse_testcase(testcase, project_mapping):
|
||||||
|
|||||||
+141
-7
@@ -212,7 +212,7 @@ class TestParser(unittest.TestCase):
|
|||||||
"/abc/def/abc"
|
"/abc/def/abc"
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
parser.parse_string_variables("${func($var_1, $var_2, xyz)}", variables_mapping),
|
parser.parse_string_variables("${func($var_1, $var_2, xyz)}", variables_mapping, {}),
|
||||||
"${func(abc, def, xyz)}"
|
"${func(abc, def, xyz)}"
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
@@ -483,7 +483,7 @@ class TestParser(unittest.TestCase):
|
|||||||
parsed_tests_mapping = parser.parse_tests(tests_mapping)
|
parsed_tests_mapping = parser.parse_tests(tests_mapping)
|
||||||
test_dict1_variables = parsed_tests_mapping["testcases"][0]["teststeps"][0]["variables"]
|
test_dict1_variables = parsed_tests_mapping["testcases"][0]["teststeps"][0]["variables"]
|
||||||
self.assertEqual(test_dict1_variables["creator"], "user_test_001")
|
self.assertEqual(test_dict1_variables["creator"], "user_test_001")
|
||||||
self.assertEqual(test_dict1_variables["username"], "$creator")
|
self.assertEqual(test_dict1_variables["username"], "user_test_001")
|
||||||
|
|
||||||
def test_parse_tests_base_url_priority(self):
|
def test_parse_tests_base_url_priority(self):
|
||||||
""" base_url & verify: priority test_dict > config
|
""" base_url & verify: priority test_dict > config
|
||||||
@@ -495,7 +495,7 @@ class TestParser(unittest.TestCase):
|
|||||||
'name': '',
|
'name': '',
|
||||||
"base_url": "$host",
|
"base_url": "$host",
|
||||||
'variables': {
|
'variables': {
|
||||||
"host": "https://debugtalk"
|
"host": "https://debugtalk.com"
|
||||||
},
|
},
|
||||||
"verify": False
|
"verify": False
|
||||||
},
|
},
|
||||||
@@ -522,7 +522,7 @@ class TestParser(unittest.TestCase):
|
|||||||
'name': '',
|
'name': '',
|
||||||
"base_url": "$host1",
|
"base_url": "$host1",
|
||||||
'variables': {
|
'variables': {
|
||||||
"host1": "https://debugtalk"
|
"host1": "https://debugtalk.com"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"teststeps": [
|
"teststeps": [
|
||||||
@@ -549,7 +549,7 @@ class TestParser(unittest.TestCase):
|
|||||||
'name': '',
|
'name': '',
|
||||||
"base_url": "$host1",
|
"base_url": "$host1",
|
||||||
'variables': {
|
'variables': {
|
||||||
"host1": "https://debugtalk"
|
"host1": "https://debugtalk.com"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"teststeps": [
|
"teststeps": [
|
||||||
@@ -569,6 +569,140 @@ class TestParser(unittest.TestCase):
|
|||||||
test_dict = parsed_tests_mapping["testcases"][0]["teststeps"][0]
|
test_dict = parsed_tests_mapping["testcases"][0]["teststeps"][0]
|
||||||
self.assertEqual(test_dict["request"]["url"], "https://httprunner.org/api1")
|
self.assertEqual(test_dict["request"]["url"], "https://httprunner.org/api1")
|
||||||
|
|
||||||
|
def test_parse_data_with_variables(self):
|
||||||
|
variables = {
|
||||||
|
"host2": "https://httprunner.org",
|
||||||
|
"num3": "${sum_two($num2, 4)}",
|
||||||
|
"num2": "${sum_two($num1, 3)}",
|
||||||
|
"num1": "${sum_two(1, 2)}"
|
||||||
|
}
|
||||||
|
from tests.debugtalk import sum_two
|
||||||
|
functions = {
|
||||||
|
"sum_two": sum_two
|
||||||
|
}
|
||||||
|
parsed_testcase = parser.parse_data(variables, variables, functions)
|
||||||
|
self.assertEqual(parsed_testcase["num3"], 10)
|
||||||
|
self.assertEqual(parsed_testcase["num2"], 6)
|
||||||
|
self.assertEqual(parsed_testcase["num1"], 3)
|
||||||
|
|
||||||
|
def test_parse_data_with_variables_not_found(self):
|
||||||
|
variables = {
|
||||||
|
"host": "https://httprunner.org",
|
||||||
|
"num4": "${sum_two($num0, 5)}",
|
||||||
|
"num3": "${sum_two($num2, 4)}",
|
||||||
|
"num2": "${sum_two($num1, 3)}",
|
||||||
|
"num1": "${sum_two(1, 2)}"
|
||||||
|
}
|
||||||
|
from tests.debugtalk import sum_two
|
||||||
|
functions = {
|
||||||
|
"sum_two": sum_two
|
||||||
|
}
|
||||||
|
with self.assertRaises(exceptions.VariableNotFound):
|
||||||
|
parser.parse_data(variables, variables, functions)
|
||||||
|
|
||||||
|
parsed_testcase = parser.parse_data(
|
||||||
|
variables,
|
||||||
|
variables,
|
||||||
|
functions,
|
||||||
|
raise_if_variable_not_found=False
|
||||||
|
)
|
||||||
|
self.assertEqual(parsed_testcase["num3"], 10)
|
||||||
|
self.assertEqual(parsed_testcase["num2"], 6)
|
||||||
|
self.assertEqual(parsed_testcase["num1"], 3)
|
||||||
|
self.assertEqual(parsed_testcase["num4"], "${sum_two($num0, 5)}")
|
||||||
|
|
||||||
|
def test_parse_tests_variable_with_function(self):
|
||||||
|
from tests.debugtalk import sum_two
|
||||||
|
tests_mapping = {
|
||||||
|
"project_mapping": {
|
||||||
|
"functions": {
|
||||||
|
"sum_two": sum_two
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'testcases': [
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
'name': '',
|
||||||
|
"base_url": "$host1",
|
||||||
|
'variables': {
|
||||||
|
"host1": "https://debugtalk.com"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"teststeps": [
|
||||||
|
{
|
||||||
|
'name': 'testcase1',
|
||||||
|
"base_url": "$host2",
|
||||||
|
"variables": {
|
||||||
|
"host2": "https://httprunner.org",
|
||||||
|
"num3": "${sum_two($num2, 4)}",
|
||||||
|
"num2": "${sum_two($num1, 3)}",
|
||||||
|
"num1": "${sum_two(1, 2)}"
|
||||||
|
},
|
||||||
|
'request': {
|
||||||
|
'url': '/api1/?num1=$num1&num2=$num2&num3=$num3',
|
||||||
|
'method': 'GET'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
parsed_tests_mapping = parser.parse_tests(tests_mapping)
|
||||||
|
test_dict = parsed_tests_mapping["testcases"][0]["teststeps"][0]
|
||||||
|
self.assertEqual(test_dict["variables"]["num3"], 10)
|
||||||
|
self.assertEqual(test_dict["variables"]["num2"], 6)
|
||||||
|
self.assertEqual(
|
||||||
|
test_dict["request"]["url"],
|
||||||
|
"https://httprunner.org/api1/?num1=3&num2=6&num3=10"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_parse_tests_variable_not_found(self):
|
||||||
|
from tests.debugtalk import sum_two
|
||||||
|
tests_mapping = {
|
||||||
|
"project_mapping": {
|
||||||
|
"functions": {
|
||||||
|
"sum_two": sum_two
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'testcases': [
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
'name': '',
|
||||||
|
"base_url": "$host1",
|
||||||
|
'variables': {
|
||||||
|
"host1": "https://debugtalk.com"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"teststeps": [
|
||||||
|
{
|
||||||
|
'name': 'testcase1',
|
||||||
|
"base_url": "$host2",
|
||||||
|
"variables": {
|
||||||
|
"host2": "https://httprunner.org",
|
||||||
|
"num4": "${sum_two($num0, 5)}",
|
||||||
|
"num3": "${sum_two($num2, 4)}",
|
||||||
|
"num2": "${sum_two($num1, 3)}",
|
||||||
|
"num1": "${sum_two(1, 2)}"
|
||||||
|
},
|
||||||
|
'request': {
|
||||||
|
'url': '/api1/?num1=$num1&num2=$num2&num3=$num3&num4=$num4',
|
||||||
|
'method': 'GET'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
parsed_tests_mapping = parser.parse_tests(tests_mapping)
|
||||||
|
test_dict = parsed_tests_mapping["testcases"][0]["teststeps"][0]
|
||||||
|
self.assertEqual(test_dict["variables"]["num3"], 10)
|
||||||
|
self.assertEqual(test_dict["variables"]["num2"], 6)
|
||||||
|
self.assertEqual(test_dict["variables"]["num4"], "${sum_two($num0, 5)}")
|
||||||
|
self.assertEqual(
|
||||||
|
test_dict["request"]["url"],
|
||||||
|
"https://httprunner.org/api1/?num1=$num1&num2=$num2&num3=$num3&num4=$num4"
|
||||||
|
)
|
||||||
|
|
||||||
def test_parse_tests_base_url_teststep_empty(self):
|
def test_parse_tests_base_url_teststep_empty(self):
|
||||||
""" base_url & verify: priority test_dict > config
|
""" base_url & verify: priority test_dict > config
|
||||||
"""
|
"""
|
||||||
@@ -579,7 +713,7 @@ class TestParser(unittest.TestCase):
|
|||||||
'name': '',
|
'name': '',
|
||||||
"base_url": "$host",
|
"base_url": "$host",
|
||||||
'variables': {
|
'variables': {
|
||||||
"host": "https://debugtalk"
|
"host": "https://debugtalk.com"
|
||||||
},
|
},
|
||||||
"verify": False
|
"verify": False
|
||||||
},
|
},
|
||||||
@@ -595,7 +729,7 @@ class TestParser(unittest.TestCase):
|
|||||||
}
|
}
|
||||||
parsed_tests_mapping = parser.parse_tests(tests_mapping)
|
parsed_tests_mapping = parser.parse_tests(tests_mapping)
|
||||||
test_dict = parsed_tests_mapping["testcases"][0]["teststeps"][0]
|
test_dict = parsed_tests_mapping["testcases"][0]["teststeps"][0]
|
||||||
self.assertEqual(test_dict["request"]["url"], "https://debugtalk/api1")
|
self.assertEqual(test_dict["request"]["url"], "https://debugtalk.com/api1")
|
||||||
self.assertEqual(test_dict["request"]["verify"], True)
|
self.assertEqual(test_dict["request"]["verify"], True)
|
||||||
|
|
||||||
def test_parse_environ(self):
|
def test_parse_environ(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user