diff --git a/ate/context.py b/ate/context.py index c5f3bded..ebd21238 100644 --- a/ate/context.py +++ b/ate/context.py @@ -88,7 +88,11 @@ class Context(object): """ for variable_bind in variable_binds: for variable_name, value in variable_bind.items(): - variable_evale_value = self.get_eval_value(value) + variable_evale_value = testcase.parse_content_with_bindings( + value, + self.testcase_variables_mapping, + self.testcase_functions_config + ) if level == "testset": self.testset_shared_variables_mapping[variable_name] = variable_evale_value @@ -126,48 +130,13 @@ class Context(object): def get_parsed_request(self): """ get parsed request, with each variable replaced by bind value. """ - parsed_request = testcase.parse_template( + parsed_request = testcase.parse_content_with_bindings( self.testcase_request_config, - self.testcase_variables_mapping + self.testcase_variables_mapping, + self.testcase_functions_config ) return parsed_request def get_testcase_variables_mapping(self): return self.testcase_variables_mapping - - def get_eval_value(self, data): - """ evaluate data recursively, each variable in data will be evaluated. - """ - if isinstance(data, (list, tuple)): - return [self.get_eval_value(item) for item in data] - - if isinstance(data, dict): - evaluated_data = {} - for key, value in data.items(): - evaluated_data[key] = self.get_eval_value(value) - - return evaluated_data - - if isinstance(data, (int, float)): - return data - - # data is in string format here - data = "" if data is None else data.strip() - - if utils.is_functon(data): - # function marker: ${func(1, 2, a=3, b=4)} - fuction_meta = utils.parse_function(data) - func_name = fuction_meta['func_name'] - args = fuction_meta.get('args', []) - kwargs = fuction_meta.get('kwargs', {}) - args = self.get_eval_value(args) - kwargs = self.get_eval_value(kwargs) - return self.testcase_functions_config[func_name](*args, **kwargs) - - elif utils.get_contain_variables(data): - parsed_data = utils.parse_variables(data, self.testcase_variables_mapping) - return parsed_data - - else: - return data diff --git a/ate/testcase.py b/ate/testcase.py index c4c58769..55f5f107 100644 --- a/ate/testcase.py +++ b/ate/testcase.py @@ -1,77 +1,87 @@ -import re from ate.exception import ParamsError -from ate.utils import string_type +from ate import utils -def parse_content_with_variables(content, variables_binds): - """ replace variables with bind value - """ - # check if content includes $variable - matched = re.match(r"^(.*)\$(\w+)(.*)$", content) - if matched: - # this is a variable, and will replace with its bind value - variable_name = matched.group(2) - value = variables_binds.get(variable_name) - if value is None: - raise ParamsError( - "%s is not defined in bind variables!" % variable_name) - if matched.group(1) or matched.group(3): - # e.g. /api/users/$uid - return content.replace("$%s" % variable_name, value) +def parse_content_with_bindings(content, variables_binds, functions_binds): + """ evaluate content recursively, each variable in content will be + evaluated with bind variables and functions. - return value - - return content - -def parse_template(testcase_template, variables_binds): - """ parse testcase_template, replace all variables with bind value. variables marker: $variable. - @param (dict) testcase_template + @param (dict) content in any data structure { "url": "http://127.0.0.1:5000/api/users/$uid", "method": "POST", "headers": { "Content-Type": "application/json", "authorization": "$authorization", - "random": "$random" + "random": "$random", + "sum": "${add_two_nums(1, 2)}" }, "body": "$data" } - @param (dict) variables binds mapping + @param (dict) variables_binds, variables binds mapping { "authorization": "a83de0ff8d2e896dbd8efb81ba14e17d", "random": "A2dEx", - "data": '{"name": "user", "password": "123456"}' + "data": {"name": "user", "password": "123456"} } - @return (dict) parsed testcase with bind variable values + @param (dict) functions_binds, functions binds mapping + { + "add_two_nums": lambda a, b=1: a + b + } + @return (dict) parsed content with evaluated bind values { "url": "http://127.0.0.1:5000/api/users/1000", "method": "POST", "headers": { "Content-Type": "application/json", "authorization": "a83de0ff8d2e896dbd8efb81ba14e17d", - "random": "A2dEx" + "random": "A2dEx", + "sum": 3 }, - "body": '{"name": "user", "password": "123456"}' + "body": {"name": "user", "password": "123456"} } """ - def substitute(content): - """ substitute content recursively, each variable will be replaced with bind value. - """ - if isinstance(content, string_type): - return parse_content_with_variables(content, variables_binds) + if isinstance(content, (list, tuple)): + return [ + parse_content_with_bindings(item, variables_binds, functions_binds) + for item in content + ] - if isinstance(content, list): - return [substitute(item) for item in content] + if isinstance(content, dict): + evaluated_data = {} + for key, value in content.items(): + evaluated_data[key] = parse_content_with_bindings( + value, variables_binds, functions_binds) - if isinstance(content, dict): - parsed_content = {} - for key, value in content.items(): - parsed_content[key] = substitute(value) - - return parsed_content + return evaluated_data + if isinstance(content, (int, float)): return content - return substitute(testcase_template) + # content is in string format here + content = "" if content is None else content.strip() + + if utils.is_functon(content): + # function marker: ${func(1, 2, a=3, b=4)} + fuction_meta = utils.parse_function(content) + func_name = fuction_meta['func_name'] + + func = functions_binds.get(func_name) + if func is None: + raise ParamsError( + "%s is not defined in bind functions!" % func_name) + + args = fuction_meta.get('args', []) + kwargs = fuction_meta.get('kwargs', {}) + args = parse_content_with_bindings(args, variables_binds, functions_binds) + kwargs = parse_content_with_bindings(kwargs, variables_binds, functions_binds) + return func(*args, **kwargs) + + elif utils.get_contain_variables(content): + parsed_data = utils.parse_variables(content, variables_binds) + return parsed_data + + else: + return content diff --git a/tests/test_context.py b/tests/test_context.py index d7ae5d96..0205de02 100644 --- a/tests/test_context.py +++ b/tests/test_context.py @@ -188,31 +188,3 @@ class VariableBindsUnittest(unittest.TestCase): self.assertEqual(len(parsed_request["headers"]["random"]), 5) self.assertIn("data", parsed_request) self.assertEqual(parsed_request["data"], testcase["variable_binds"][2]["data"]) - - def test_get_eval_value(self): - self.context.testcase_variables_mapping = { - "str_1": "str_value1", - "str_2": "str_value2" - } - self.assertEqual(self.context.get_eval_value("$str_1"), "str_value1") - self.assertEqual(self.context.get_eval_value("$str_2"), "str_value2") - self.assertEqual( - self.context.get_eval_value(["$str_1", "str3"]), - ["str_value1", "str3"] - ) - self.assertEqual( - self.context.get_eval_value({"key": "$str_1"}), - {"key": "str_value1"} - ) - - import random, string - self.context.testcase_functions_config["gen_random_string"] = \ - lambda str_len: ''.join(random.choice(string.ascii_letters + string.digits) \ - for _ in range(str_len)) - result = self.context.get_eval_value("${gen_random_string(5)}") - self.assertEqual(len(result), 5) - - add_two_nums = lambda a, b=1: a + b - self.context.testcase_functions_config["add_two_nums"] = add_two_nums - self.assertEqual(self.context.get_eval_value("${add_two_nums(1)}"), 2) - self.assertEqual(self.context.get_eval_value("${add_two_nums(1, 2)}"), 3) diff --git a/tests/test_testcase.py b/tests/test_testcase.py index d32c9a3e..7f46fe19 100644 --- a/tests/test_testcase.py +++ b/tests/test_testcase.py @@ -1,120 +1,111 @@ import unittest -from ate.testcase import parse_template, parse_content_with_variables -from ate import exception +from ate.exception import ParamsError +from ate.testcase import parse_content_with_bindings class TestcaseParserUnittest(unittest.TestCase): - def setUp(self): - self.variables_binds = { + def test_parse_content_with_bindings_variables(self): + variables_binds = { + "str_1": "str_value1", + "str_2": "str_value2" + } + self.assertEqual( + parse_content_with_bindings("$str_1", variables_binds, {}), + "str_value1" + ) + self.assertEqual( + parse_content_with_bindings("123$str_1/456", variables_binds, {}), + "123str_value1/456" + ) + + with self.assertRaises(ParamsError): + parse_content_with_bindings("$str_3", variables_binds, {}) + + self.assertEqual( + parse_content_with_bindings(["$str_1", "str3"], variables_binds, {}), + ["str_value1", "str3"] + ) + self.assertEqual( + parse_content_with_bindings({"key": "$str_1"}, variables_binds, {}), + {"key": "str_value1"} + ) + + def test_parse_content_with_bindings_multiple_identical_variables(self): + variables_binds = { + "userid": 100, + "data": 1498 + } + content = "/users/$userid/training/$data?userId=$userid&data=$data" + self.assertEqual( + parse_content_with_bindings(content, variables_binds, {}), + "/users/100/training/1498?userId=100&data=1498" + ) + + def test_parse_content_with_bindings_functions(self): + import random, string + functions_binds = { + "gen_random_string": lambda str_len: ''.join(random.choice(string.ascii_letters + string.digits) \ + for _ in range(str_len)) + } + + result = parse_content_with_bindings("${gen_random_string(5)}", {}, functions_binds) + self.assertEqual(len(result), 5) + + add_two_nums = lambda a, b=1: a + b + functions_binds["add_two_nums"] = add_two_nums + self.assertEqual( + parse_content_with_bindings("${add_two_nums(1)}", {}, functions_binds), + 2 + ) + self.assertEqual( + parse_content_with_bindings("${add_two_nums(1, 2)}", {}, functions_binds), + 3 + ) + + def test_parse_content_with_bindings_testcase(self): + variables_binds = { "uid": "1000", "random": "A2dEx", "authorization": "a83de0ff8d2e896dbd8efb81ba14e17d", - "json": { - "name": "user1", - "password": "123456" - }, + "data": {"name": "user", "password": "123456"}, "expected_status": 201, "expected_success": True } - - def test_parse_testcase_template(self): + functions_binds = { + "add_two_nums": lambda a, b=1: a + b + } testcase = { - "request": { - "url": "http://127.0.0.1:5000/api/users/$uid", - "method": "POST", - "headers": { - "Content-Type": "application/json", - "authorization": "$authorization", - "random": "$random" - }, - "body": "$json" + "url": "http://127.0.0.1:5000/api/users/$uid", + "method": "POST", + "headers": { + "Content-Type": "application/json", + "authorization": "$authorization", + "random": "$random", + "sum": "${add_two_nums(1, 2)}" }, - "response": { - "status_code": "$expected_status", - "headers": { - "Content-Type": "application/json" - }, - "body": { - "success": "$expected_success", - "msg": "user created successfully." - } - } + "body": "$data" } - parsed_testcase = parse_template(testcase, self.variables_binds) + parsed_testcase = parse_content_with_bindings(testcase, variables_binds, functions_binds) self.assertEqual( - parsed_testcase["request"]["url"], - "http://127.0.0.1:5000/api/users/%s" % self.variables_binds["uid"] + parsed_testcase["url"], + "http://127.0.0.1:5000/api/users/%s" % variables_binds["uid"] ) self.assertEqual( - parsed_testcase["request"]["headers"]["authorization"], - self.variables_binds["authorization"] + parsed_testcase["headers"]["authorization"], + variables_binds["authorization"] ) self.assertEqual( - parsed_testcase["request"]["headers"]["random"], - self.variables_binds["random"] + parsed_testcase["headers"]["random"], + variables_binds["random"] ) self.assertEqual( - parsed_testcase["request"]["body"], - self.variables_binds["json"] + parsed_testcase["body"], + variables_binds["data"] ) self.assertEqual( - parsed_testcase["response"]["status_code"], - self.variables_binds["expected_status"] + parsed_testcase["headers"]["sum"], + 3 ) - self.assertEqual( - parsed_testcase["response"]["body"]["success"], - self.variables_binds["expected_success"] - ) - - def test_parse_testcase_template_miss_bind_variable(self): - testcase = { - "request": { - "url": "http://127.0.0.1:5000/api/users/$uid", - "method": "$method" - } - } - with self.assertRaises(exception.ParamsError): - parse_template(testcase, self.variables_binds) - - def test_parse_testcase_with_new_variable_binds(self): - testcase = { - "request": { - "url": "http://127.0.0.1:5000/api/users/$uid", - "method": "$method" - } - } - new_variable_binds = { - "method": "GET" - } - self.variables_binds.update(new_variable_binds) - parsed_testcase = parse_template(testcase, self.variables_binds) - - self.assertEqual( - parsed_testcase["request"]["method"], - new_variable_binds["method"] - ) - - def test_parse_content_with_variables(self): - content = "$var" - variables_binds = { - "var": "abc" - } - result = parse_content_with_variables(content, variables_binds) - self.assertEqual(result, "abc") - - content = "123$var/456" - variables_binds = { - "var": "abc" - } - result = parse_content_with_variables(content, variables_binds) - self.assertEqual(result, "123abc/456") - - content = "$var1" - variables_binds = { - "var2": "abc" - } - with self.assertRaises(exception.ParamsError): - parse_content_with_variables(content, variables_binds)