mirror of
https://github.com/httprunner/httprunner.git
synced 2026-09-05 23:51:25 +08:00
refactor parameterization:
parameter value now can be in three types: (1) data list (2) call built-in parameterize function (3) call custom function in debugtalk.py
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
__title__ = 'HttpRunner'
|
__title__ = 'HttpRunner'
|
||||||
__description__ = 'HTTP test runner, not just about api test and load test.'
|
__description__ = 'HTTP test runner, not just about api test and load test.'
|
||||||
__url__ = 'https://github.com/HttpRunner/HttpRunner'
|
__url__ = 'https://github.com/HttpRunner/HttpRunner'
|
||||||
__version__ = '0.9.9'
|
__version__ = '1.0.0'
|
||||||
__author__ = 'debugtalk'
|
__author__ = 'debugtalk'
|
||||||
__author_email__ = 'mail@debugtalk.com'
|
__author_email__ = 'mail@debugtalk.com'
|
||||||
__license__ = 'MIT'
|
__license__ = 'MIT'
|
||||||
|
|||||||
+1
-1
@@ -59,7 +59,7 @@ class TestSuite(unittest.TestSuite):
|
|||||||
self.config_dict["variables"] = utils.override_variables_binds(variables, variables_mapping)
|
self.config_dict["variables"] = utils.override_variables_binds(variables, variables_mapping)
|
||||||
|
|
||||||
parameters = self.config_dict.get("parameters", [])
|
parameters = self.config_dict.get("parameters", [])
|
||||||
cartesian_product_parameters = testcase.gen_cartesian_product_parameters(
|
cartesian_product_parameters = testcase.parse_parameters(
|
||||||
parameters,
|
parameters,
|
||||||
self.config_dict["path"]
|
self.config_dict["path"]
|
||||||
) or [{}]
|
) or [{}]
|
||||||
|
|||||||
+57
-26
@@ -11,8 +11,8 @@ import yaml
|
|||||||
from httprunner import exception, logger, utils
|
from httprunner import exception, logger, utils
|
||||||
|
|
||||||
variable_regexp = r"\$([\w_]+)"
|
variable_regexp = r"\$([\w_]+)"
|
||||||
function_regexp = r"\$\{([\w_]+\([\$\w_ =,]*\))\}"
|
function_regexp = r"\$\{([\w_]+\([\$\w\.\-_ =,]*\))\}"
|
||||||
function_regexp_compile = re.compile(r"^([\w_]+)\(([\$\w_ =,]*)\)$")
|
function_regexp_compile = re.compile(r"^([\w_]+)\(([\$\w\.\-_ =,]*)\)$")
|
||||||
test_def_overall_dict = {
|
test_def_overall_dict = {
|
||||||
"loaded": False,
|
"loaded": False,
|
||||||
"api": {},
|
"api": {},
|
||||||
@@ -73,10 +73,6 @@ def _load_csv_file(csv_file):
|
|||||||
|
|
||||||
if not parameter_list:
|
if not parameter_list:
|
||||||
# first line will always be parameter name
|
# first line will always be parameter name
|
||||||
expected_filename = "{}.csv".format("-".join(line_data))
|
|
||||||
if not csv_file.endswith(expected_filename):
|
|
||||||
raise exception.FileFormatError("CSV file name does not match with headers: {}".format(csv_file))
|
|
||||||
|
|
||||||
parameter_list = line_data
|
parameter_list = line_data
|
||||||
collums_num = len(parameter_list)
|
collums_num = len(parameter_list)
|
||||||
continue
|
continue
|
||||||
@@ -642,34 +638,54 @@ def gen_cartesian_product(*args):
|
|||||||
|
|
||||||
return product_list
|
return product_list
|
||||||
|
|
||||||
def gen_cartesian_product_parameters(parameters, testset_path):
|
def parse_parameters(parameters, testset_path=None):
|
||||||
""" parse parameters and generate cartesian product
|
""" parse parameters and generate cartesian product
|
||||||
@params
|
@params
|
||||||
(list) parameters: parameter name and fetch method
|
(list) parameters: parameter name and value in list
|
||||||
|
parameter value may be in three types:
|
||||||
|
(1) data list
|
||||||
|
(2) call built-in parameterize function
|
||||||
|
(3) call custom function in debugtalk.py
|
||||||
e.g.
|
e.g.
|
||||||
[
|
[
|
||||||
{"user_agent": "Random"},
|
{"user_agent": ["iOS/10.1", "iOS/10.2", "iOS/10.3"]},
|
||||||
{"app_version": "Sequential"}
|
{"username-password": "${parameterize(account.csv)}"},
|
||||||
|
{"app_version": "${gen_app_version()}"}
|
||||||
]
|
]
|
||||||
(str) testset_path: testset file path, used for locating csv file
|
(str) testset_path: testset file path, used for locating csv file and debugtalk.py
|
||||||
@return cartesian product in list
|
@return cartesian product in list
|
||||||
"""
|
"""
|
||||||
parameters_content_list = []
|
testcase_parser = TestcaseParser(file_path=testset_path)
|
||||||
|
|
||||||
|
parsed_parameters_list = []
|
||||||
for parameter in parameters:
|
for parameter in parameters:
|
||||||
parameter_name, fetch_method = list(parameter.items())[0]
|
parameter_name, parameter_content = list(parameter.items())[0]
|
||||||
parameter_file_path = os.path.join(
|
parameter_name_list = parameter_name.split("-")
|
||||||
os.path.dirname(testset_path),
|
|
||||||
"{}.csv".format(parameter_name)
|
|
||||||
)
|
|
||||||
csv_content_list = load_file(parameter_file_path)
|
|
||||||
|
|
||||||
if fetch_method.lower() == "random":
|
if isinstance(parameter_content, list):
|
||||||
random.shuffle(csv_content_list)
|
# (1) data list
|
||||||
|
# e.g. {"app_version": ["2.8.5", "2.8.6"]}
|
||||||
|
# => [{"app_version": "2.8.5", "app_version": "2.8.6"}]
|
||||||
|
# e.g. {"username-password": [["user1", "111111"], ["test2", "222222"]}
|
||||||
|
# => [{"username": "user1", "password": "111111"}, {"username": "user2", "password": "222222"}]
|
||||||
|
parameter_content_list = [
|
||||||
|
dict(zip(parameter_name_list, [parameter_item]))
|
||||||
|
for parameter_item in parameter_content
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
# (2) & (3)
|
||||||
|
parsed_parameter_content = testcase_parser.eval_content_with_bindings(parameter_content)
|
||||||
|
# e.g. [{'app_version': '2.8.5'}, {'app_version': '2.8.6'}]
|
||||||
|
# e.g. [{"username": "user1", "password": "111111"}, {"username": "user2", "password": "222222"}]
|
||||||
|
parameter_content_list = [
|
||||||
|
# get subset by parameter name
|
||||||
|
{key: parameter_item[key] for key in parameter_name_list}
|
||||||
|
for parameter_item in parsed_parameter_content
|
||||||
|
]
|
||||||
|
|
||||||
parameters_content_list.append(csv_content_list)
|
parsed_parameters_list.append(parameter_content_list)
|
||||||
|
|
||||||
return gen_cartesian_product(*parameters_content_list)
|
|
||||||
|
|
||||||
|
return gen_cartesian_product(*parsed_parameters_list)
|
||||||
|
|
||||||
class TestcaseParser(object):
|
class TestcaseParser(object):
|
||||||
|
|
||||||
@@ -726,19 +742,34 @@ class TestcaseParser(object):
|
|||||||
raise exception.ParamsError(
|
raise exception.ParamsError(
|
||||||
"{} is not defined in bind {}s!".format(item_name, item_type))
|
"{} is not defined in bind {}s!".format(item_name, item_type))
|
||||||
|
|
||||||
|
def parameterize(self, csv_file_name, fetch_method="Sequential"):
|
||||||
|
parameter_file_path = os.path.join(
|
||||||
|
os.path.dirname(self.file_path),
|
||||||
|
"{}".format(csv_file_name)
|
||||||
|
)
|
||||||
|
csv_content_list = load_file(parameter_file_path)
|
||||||
|
|
||||||
|
if fetch_method.lower() == "random":
|
||||||
|
random.shuffle(csv_content_list)
|
||||||
|
|
||||||
|
return csv_content_list
|
||||||
|
|
||||||
def _eval_content_functions(self, content):
|
def _eval_content_functions(self, content):
|
||||||
functions_list = extract_functions(content)
|
functions_list = extract_functions(content)
|
||||||
for func_content in functions_list:
|
for func_content in functions_list:
|
||||||
function_meta = parse_function(func_content)
|
function_meta = parse_function(func_content)
|
||||||
func_name = function_meta['func_name']
|
func_name = function_meta['func_name']
|
||||||
|
|
||||||
func = self.get_bind_item("function", func_name)
|
|
||||||
|
|
||||||
args = function_meta.get('args', [])
|
args = function_meta.get('args', [])
|
||||||
kwargs = function_meta.get('kwargs', {})
|
kwargs = function_meta.get('kwargs', {})
|
||||||
args = self.eval_content_with_bindings(args)
|
args = self.eval_content_with_bindings(args)
|
||||||
kwargs = self.eval_content_with_bindings(kwargs)
|
kwargs = self.eval_content_with_bindings(kwargs)
|
||||||
eval_value = func(*args, **kwargs)
|
|
||||||
|
if func_name in ["parameterize", "P"]:
|
||||||
|
eval_value = self.parameterize(*args, **kwargs)
|
||||||
|
else:
|
||||||
|
func = self.get_bind_item("function", func_name)
|
||||||
|
eval_value = func(*args, **kwargs)
|
||||||
|
|
||||||
func_content = "${" + func_content + "}"
|
func_content = "${" + func_content + "}"
|
||||||
if func_content == content:
|
if func_content == content:
|
||||||
|
|||||||
@@ -48,3 +48,15 @@ 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"
|
||||||
|
|
||||||
|
def gen_app_version():
|
||||||
|
return [
|
||||||
|
{"app_version": "2.8.5"},
|
||||||
|
{"app_version": "2.8.6"}
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_account():
|
||||||
|
return [
|
||||||
|
{"username": "user1", "password": "111111"},
|
||||||
|
{"username": "user2", "password": "222222"}
|
||||||
|
]
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
- config:
|
- config:
|
||||||
name: "user management testset."
|
name: "user management testset."
|
||||||
parameters:
|
parameters:
|
||||||
- user_agent: Random
|
- user_agent: ["iOS/10.1", "iOS/10.2", "iOS/10.3"]
|
||||||
- app_version: Sequential
|
- app_version: ${gen_app_version()}
|
||||||
|
- username: ${parameterize(account.csv)}
|
||||||
variables:
|
variables:
|
||||||
- user_agent: 'iOS/10.3'
|
- user_agent: 'iOS/10.3'
|
||||||
- device_sn: ${gen_random_string(15)}
|
- device_sn: ${gen_random_string(15)}
|
||||||
- os_platform: 'ios'
|
- os_platform: 'ios'
|
||||||
- app_version: '2.8.6'
|
|
||||||
request:
|
request:
|
||||||
base_url: $BASE_URL
|
base_url: $BASE_URL
|
||||||
headers:
|
headers:
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
- token
|
- token
|
||||||
|
|
||||||
- test:
|
- test:
|
||||||
name: get token with $user_agent and $app_version
|
name: get token with $user_agent and $app_version, username $username
|
||||||
api: get_token($user_agent, $device_sn, $os_platform, $app_version)
|
api: get_token($user_agent, $device_sn, $os_platform, $app_version)
|
||||||
extract:
|
extract:
|
||||||
- token: content.token
|
- token: content.token
|
||||||
|
|||||||
@@ -164,4 +164,4 @@ class TestRunner(ApiServerUnittest):
|
|||||||
result = HttpRunner(testcase_file_path).run()
|
result = HttpRunner(testcase_file_path).run()
|
||||||
self.assertTrue(result["success"])
|
self.assertTrue(result["success"])
|
||||||
self.assertIn("token", result["output"])
|
self.assertIn("token", result["output"])
|
||||||
self.assertEqual(result["stat"]["testsRun"], 6)
|
self.assertEqual(result["stat"]["testsRun"], 3 * 2 * 3)
|
||||||
|
|||||||
+48
-18
@@ -51,7 +51,7 @@ class TestcaseParserUnittest(unittest.TestCase):
|
|||||||
|
|
||||||
def test_load_csv_file_multiple_parameters(self):
|
def test_load_csv_file_multiple_parameters(self):
|
||||||
csv_file_path = os.path.join(
|
csv_file_path = os.path.join(
|
||||||
os.getcwd(), 'tests/data/username-password.csv')
|
os.getcwd(), 'tests/data/account.csv')
|
||||||
csv_content = testcase.load_file(csv_file_path)
|
csv_content = testcase.load_file(csv_file_path)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
csv_content,
|
csv_content,
|
||||||
@@ -105,40 +105,70 @@ class TestcaseParserUnittest(unittest.TestCase):
|
|||||||
product_list = testcase.gen_cartesian_product(*parameters_content_list)
|
product_list = testcase.gen_cartesian_product(*parameters_content_list)
|
||||||
self.assertEqual(product_list, [])
|
self.assertEqual(product_list, [])
|
||||||
|
|
||||||
def test_gen_cartesian_product_parameters_one_to_one(self):
|
def test_parse_parameters_raw_list(self):
|
||||||
parameters = [
|
parameters = [
|
||||||
{"user_agent": "random"},
|
{"user_agent": ["iOS/10.1", "iOS/10.2", "iOS/10.3"]},
|
||||||
{"app_version": "sequential"}
|
{"username-password": [("user1", "111111"), ("test2", "222222")]}
|
||||||
]
|
]
|
||||||
testset_path = os.path.join(
|
cartesian_product_parameters = testcase.parse_parameters(parameters)
|
||||||
os.getcwd(),
|
|
||||||
"tests/data/demo_parameters.yml"
|
|
||||||
)
|
|
||||||
cartesian_product_parameters = testcase.gen_cartesian_product_parameters(
|
|
||||||
parameters,
|
|
||||||
testset_path
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
len(cartesian_product_parameters),
|
len(cartesian_product_parameters),
|
||||||
6
|
3 * 2
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_gen_cartesian_product_parameters_one_to_multiple(self):
|
def test_parse_parameters_parameterize(self):
|
||||||
parameters = [
|
parameters = [
|
||||||
{"user_agent": "random"},
|
{"app_version": "${parameterize(app_version.csv)}"},
|
||||||
{"username-password": "sequential"}
|
{"username-password": "${parameterize(account.csv)}"}
|
||||||
]
|
]
|
||||||
testset_path = os.path.join(
|
testset_path = os.path.join(
|
||||||
os.getcwd(),
|
os.getcwd(),
|
||||||
"tests/data/demo_parameters.yml"
|
"tests/data/demo_parameters.yml"
|
||||||
)
|
)
|
||||||
cartesian_product_parameters = testcase.gen_cartesian_product_parameters(
|
cartesian_product_parameters = testcase.parse_parameters(
|
||||||
parameters,
|
parameters,
|
||||||
testset_path
|
testset_path
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
len(cartesian_product_parameters),
|
len(cartesian_product_parameters),
|
||||||
9
|
2 * 3
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_parse_parameters_custom_function(self):
|
||||||
|
parameters = [
|
||||||
|
{"app_version": "${gen_app_version()}"},
|
||||||
|
{"username-password": "${get_account()}"}
|
||||||
|
]
|
||||||
|
testset_path = os.path.join(
|
||||||
|
os.getcwd(),
|
||||||
|
"tests/data/demo_parameters.yml"
|
||||||
|
)
|
||||||
|
cartesian_product_parameters = testcase.parse_parameters(
|
||||||
|
parameters,
|
||||||
|
testset_path
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
len(cartesian_product_parameters),
|
||||||
|
2 * 2
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_parse_parameters_mix(self):
|
||||||
|
parameters = [
|
||||||
|
{"user_agent": ["iOS/10.1", "iOS/10.2", "iOS/10.3"]},
|
||||||
|
{"app_version": "${gen_app_version()}"},
|
||||||
|
{"username-password": "${parameterize(account.csv)}"}
|
||||||
|
]
|
||||||
|
testset_path = os.path.join(
|
||||||
|
os.getcwd(),
|
||||||
|
"tests/data/demo_parameters.yml"
|
||||||
|
)
|
||||||
|
cartesian_product_parameters = testcase.parse_parameters(
|
||||||
|
parameters,
|
||||||
|
testset_path
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
len(cartesian_product_parameters),
|
||||||
|
3 * 2 * 3
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_load_yaml_file_file_format_error(self):
|
def test_load_yaml_file_file_format_error(self):
|
||||||
|
|||||||
Reference in New Issue
Block a user