refactor parameterization:

1, support parameters in test block;
2, refactor parameterization code.
This commit is contained in:
httprunner
2018-03-08 19:35:42 +08:00
parent 96fc410234
commit 6ca5e63377
6 changed files with 132 additions and 52 deletions
+1 -1
View File
@@ -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__ = '1.0.0' __version__ = '1.1.0-beta'
__author__ = 'debugtalk' __author__ = 'debugtalk'
__author_email__ = 'mail@debugtalk.com' __author_email__ = 'mail@debugtalk.com'
__license__ = 'MIT' __license__ = 'MIT'
+79 -27
View File
@@ -1,3 +1,4 @@
import copy
import sys import sys
import unittest import unittest
@@ -32,12 +33,15 @@ class TestSuite(unittest.TestSuite):
"name": "testset description", "name": "testset description",
"requires": [], "requires": [],
"function_binds": {}, "function_binds": {},
"parameters": {},
"variables": [], "variables": [],
"request": {} "request": {},
"output": []
}, },
"testcases": [ "testcases": [
{ {
"name": "testcase description", "name": "testcase description",
"parameters": {},
"variables": [], # optional, override "variables": [], # optional, override
"request": {}, "request": {},
"extract": {}, # optional "extract": {}, # optional
@@ -51,44 +55,92 @@ class TestSuite(unittest.TestSuite):
""" """
def __init__(self, testset, variables_mapping=None, http_client_session=None): def __init__(self, testset, variables_mapping=None, http_client_session=None):
super(TestSuite, self).__init__() super(TestSuite, self).__init__()
self.test_runner_list = []
self.config_dict = testset.get("config", {}) config_dict = testset.get("config", {})
self.output_variables_list = config_dict.get("output", [])
self.testset_file_path = config_dict["path"]
config_dict_parameters = config_dict.get("parameters", [])
variables = self.config_dict.get("variables", []) config_dict_variables = config_dict.get("variables", [])
variables_mapping = variables_mapping or {} variables_mapping = variables_mapping or {}
self.config_dict["variables"] = utils.override_variables_binds(variables, variables_mapping) config_dict_variables = utils.override_variables_binds(config_dict_variables, variables_mapping)
parameters = self.config_dict.get("parameters", []) config_parametered_variables_list = self._get_parametered_variables(
config_dict_variables,
config_dict_parameters
)
self.testcase_parser = testcase.TestcaseParser()
testcases = testset.get("testcases", [])
for config_variables in config_parametered_variables_list:
# config level
config_dict["variables"] = config_variables
test_runner = runner.Runner(config_dict, http_client_session)
for testcase_dict in testcases:
testcase_dict = copy.copy(testcase_dict)
# testcase level
testcase_parametered_variables_list = self._get_parametered_variables(
testcase_dict.get("variables", []),
testcase_dict.get("parameters", [])
)
for testcase_variables in testcase_parametered_variables_list:
testcase_dict["variables"] = testcase_variables
# eval testcase name with bind variables
variables = utils.override_variables_binds(
config_variables,
testcase_variables
)
self.testcase_parser.update_binded_variables(variables)
testcase_name = self.testcase_parser.eval_content_with_bindings(testcase_dict["name"])
self.test_runner_list.append((test_runner, variables))
self._add_test_to_suite(testcase_name, test_runner, testcase_dict)
def _get_parametered_variables(self, variables, parameters):
""" parameterize varaibles with parameters
"""
cartesian_product_parameters = testcase.parse_parameters( cartesian_product_parameters = testcase.parse_parameters(
parameters, parameters,
self.config_dict["path"] self.testset_file_path
) or [{}] ) or [{}]
parametered_variables_list = []
for parameter_mapping in cartesian_product_parameters: for parameter_mapping in cartesian_product_parameters:
if parameter_mapping: parameter_mapping = parameter_mapping or {}
self.config_dict["variables"] = utils.override_variables_binds( variables = utils.override_variables_binds(
self.config_dict["variables"], variables,
parameter_mapping parameter_mapping
) )
self.test_runner = runner.Runner(self.config_dict, http_client_session) parametered_variables_list.append(variables)
testcases = testset.get("testcases", [])
self._add_tests_to_suite(testcases)
def _add_tests_to_suite(self, testcases): return parametered_variables_list
for testcase_dict in testcases:
testcase_name = self.test_runner.context.eval_content(testcase_dict["name"])
if utils.PYTHON_VERSION == 3:
TestCase.runTest.__doc__ = testcase_name
else:
TestCase.runTest.__func__.__doc__ = testcase_name
test = TestCase(self.test_runner, testcase_dict) def _add_test_to_suite(self, testcase_name, test_runner, testcase_dict):
[self.addTest(test) for _ in range(int(testcase_dict.get("times", 1)))] if utils.PYTHON_VERSION == 3:
TestCase.runTest.__doc__ = testcase_name
else:
TestCase.runTest.__func__.__doc__ = testcase_name
test = TestCase(test_runner, testcase_dict)
[self.addTest(test) for _ in range(int(testcase_dict.get("times", 1)))]
@property @property
def output(self): def output(self):
output_variables_list = self.config_dict.get("output", []) outputs = []
return self.test_runner.extract_output(output_variables_list)
for test_runner, variables in self.test_runner_list:
outputs.append(
{
"in": variables,
"out": test_runner.extract_output(self.output_variables_list)
}
)
return outputs
class TaskSuite(unittest.TestSuite): class TaskSuite(unittest.TestSuite):
""" create task suite with specified testcase path. """ create task suite with specified testcase path.
@@ -167,9 +219,9 @@ class HttpRunner(object):
result = self.runner.run(task_suite) result = self.runner.run(task_suite)
output = {} output = []
for task in task_suite.tasks: for task in task_suite.tasks:
output.update(task.output) output.extend(task.output)
if self.gen_html_report: if self.gen_html_report:
summary = result.summary summary = result.summary
+32 -15
View File
@@ -1,3 +1,4 @@
import copy
import hashlib import hashlib
import hmac import hmac
import imp import imp
@@ -319,10 +320,11 @@ def update_ordered_dict(ordered_dict, override_mapping):
"c": 4 "c": 4
}) })
""" """
new_ordered_dict = copy.copy(ordered_dict)
for var, value in override_mapping.items(): for var, value in override_mapping.items():
ordered_dict.update({var: value}) new_ordered_dict.update({var: value})
return ordered_dict return new_ordered_dict
def override_variables_binds(variables, new_mapping): def override_variables_binds(variables, new_mapping):
""" convert variables in testcase to ordered mapping, with new_mapping overrided """ convert variables in testcase to ordered mapping, with new_mapping overrided
@@ -339,25 +341,40 @@ def override_variables_binds(variables, new_mapping):
new_mapping new_mapping
) )
def print_output(output): def print_output(outputs):
if not output:
if not outputs:
return return
content = "\n================== Output ==================\n" content = "\n================== Variables & Output ==================\n"
content += '{:<16}: {:<}\n'.format("Variable", "Value") content += '{:<6} | {:<16} : {:<}\n'.format("Type", "Variable", "Value")
content += '{:<16}: {:<}\n'.format("--------", "-----") content += '{:<6} | {:<16} : {:<}\n'.format("-" * 6, "-" * 16, "-" * 27)
for variable, value in output.items(): def prepare_content(var_type, in_out):
content = ""
for variable, value in in_out.items():
if PYTHON_VERSION == 2: if PYTHON_VERSION == 2:
if isinstance(variable, unicode): if isinstance(variable, unicode):
variable = variable.encode("utf-8") variable = variable.encode("utf-8")
if isinstance(value, unicode): if isinstance(value, unicode):
value = value.encode("utf-8") value = value.encode("utf-8")
content += '{:<16}: {:<}\n'.format(variable, value) content += '{:<6} | {:<16} : {:<}\n'.format(var_type, variable, value)
content += "============================================\n" return content
for output in outputs:
_in = output["in"]
_out = output["out"]
if not _out:
continue
content += prepare_content("Var", _in)
content += "\n"
content += prepare_content("Out", _out)
content += "-" * 56 + "\n"
logger.log_debug(content) logger.log_debug(content)
+13 -4
View File
@@ -2,10 +2,7 @@
name: "user management testset." name: "user management testset."
parameters: parameters:
- user_agent: ["iOS/10.1", "iOS/10.2", "iOS/10.3"] - user_agent: ["iOS/10.1", "iOS/10.2", "iOS/10.3"]
- app_version: ${gen_app_version()}
- username: ${parameterize(account.csv)}
variables: variables:
- user_agent: 'iOS/10.3'
- device_sn: ${gen_random_string(15)} - device_sn: ${gen_random_string(15)}
- os_platform: 'ios' - os_platform: 'ios'
request: request:
@@ -17,10 +14,22 @@
- token - token
- test: - test:
name: get token with $user_agent and $app_version, username $username name: get token with $user_agent and $app_version
parameters:
- app_version: ${gen_app_version()}
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
validate: validate:
- "eq": ["status_code", 200] - "eq": ["status_code", 200]
- "len_eq": ["content.token", 16] - "len_eq": ["content.token", 16]
# - test:
# name: create user
# parameters:
# - user_id: [1001, 1002, 1003]
# - username-password: ${parameterize(account.csv)}
# api: create_user($user_id, $username, $password, $token)
# validate:
# - {"check": "status_code", "expect": 201}
# - {"check": "content.success", "expect": true}
+1 -1
View File
@@ -14,7 +14,7 @@
- token - token
- test: - test:
name: get token name: get token with $user_agent, $app_version
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
+6 -4
View File
@@ -121,7 +121,8 @@ class TestRunner(ApiServerUnittest):
os.getcwd(), 'tests/data/demo_testset_layer.yml') os.getcwd(), 'tests/data/demo_testset_layer.yml')
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"][0]["out"])
self.assertEqual(len(result["output"]), 13)
def test_run_testset_with_variables_mapping(self): def test_run_testset_with_variables_mapping(self):
testcase_file_path = os.path.join( testcase_file_path = os.path.join(
@@ -131,7 +132,8 @@ class TestRunner(ApiServerUnittest):
} }
result = HttpRunner(testcase_file_path).run(mapping=variables_mapping) result = HttpRunner(testcase_file_path).run(mapping=variables_mapping)
self.assertTrue(result["success"]) self.assertTrue(result["success"])
self.assertIn("token", result["output"]) self.assertIn("token", result["output"][0]["out"])
self.assertEqual(len(result["output"]), 13)
def test_run_testcase_with_empty_header(self): def test_run_testcase_with_empty_header(self):
testcase_file_path = os.path.join( testcase_file_path = os.path.join(
@@ -163,5 +165,5 @@ class TestRunner(ApiServerUnittest):
os.getcwd(), 'tests/data/demo_parameters.yml') os.getcwd(), 'tests/data/demo_parameters.yml')
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.assertEqual(len(result["output"]), 3 * 2)
self.assertEqual(result["stat"]["testsRun"], 3 * 2 * 3) self.assertEqual(result["stat"]["testsRun"], 3 * 2)