diff --git a/README.md b/README.md index bc1d2b9f..d42e2fa2 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ To ensure the installation or upgrade is successful, you can execute command `at ```text $ ate -V -ApiTestEngine version: 0.6.0 +ApiTestEngine version: 0.7.0 ``` Execute the command `ate -h` to view command help. @@ -75,7 +75,7 @@ To install mail helper, run this command in your terminal: $ pip install -U git+https://github.com/debugtalk/jenkins-mail-py.git#egg=jenkins-mail-py $ ate -V jenkins-mail-py version: 0.2.5 -ApiTestEngine version: 0.6.0 +ApiTestEngine version: 0.7.0 ``` With [`jenkins-mail-py`][jenkins-mail-py] installed, you can see more optional arguments. diff --git a/ate/__init__.py b/ate/__init__.py index de5901e2..19442947 100644 --- a/ate/__init__.py +++ b/ate/__init__.py @@ -1 +1 @@ -__version__ = '0.6.3' \ No newline at end of file +__version__ = '0.7.0' \ No newline at end of file diff --git a/ate/exception.py b/ate/exception.py index fa1cc24f..028ec98b 100644 --- a/ate/exception.py +++ b/ate/exception.py @@ -24,3 +24,6 @@ class FunctionNotFound(NameError): class VariableNotFound(NameError): pass + +class ApiNotFound(NameError): + pass diff --git a/ate/utils.py b/ate/utils.py index 69ec09b5..0a5cbfce 100644 --- a/ate/utils.py +++ b/ate/utils.py @@ -11,7 +11,7 @@ import string import types import yaml -from ate import exception +from ate import exception, testcase from requests.structures import CaseInsensitiveDict try: @@ -24,6 +24,7 @@ except NameError: PYTHON_VERSION = 3 SECRET_KEY = "DebugTalk" +api_overall_dict = {} def gen_random_string(str_len): return ''.join( @@ -125,6 +126,7 @@ def load_testcases_by_path(path): "testcases": [] } testcases_list = load_testcases(path) + dir_path = os.path.dirname(os.path.abspath(path)) for item in testcases_list: for key in item: @@ -132,13 +134,76 @@ def load_testcases_by_path(path): testset["config"].update(item["config"]) testset["name"] = item["config"].get("name", "") elif key == "test": - testset["testcases"].append(item["test"]) + test_dict = item["test"] + if "api" in test_dict: + update_test_info(test_dict, dir_path) + + testset["testcases"].append(test_dict) return [testset] if testset["testcases"] else [] else: return [] +def update_test_info(test_dict, dir_path): + api_call = test_dict["api"] + function_meta = testcase.parse_function(api_call) + func_name = function_meta["func_name"] + api_info = get_api_definition(func_name, dir_path) + test_dict.update(api_info) + +def get_api_definition(name, dir_path): + """ get expected api from dir_path upward recursively + @param + name: api name + dir_path: start search dir path + @return + expected api info if found, otherwise raise ApiNotFound exception + """ + api_dir_dict = api_overall_dict.get(dir_path) + if not api_dir_dict: + api_dir_dict = load_api_definition(dir_path) + api_overall_dict[dir_path] = api_dir_dict + + api_info = api_dir_dict.get(name) + if api_info: + return api_info + + parent_dir_path = os.path.dirname(dir_path) + if dir_path == parent_dir_path: + # system root path + err_msg = "{} not found in recursive upward path!".format(name) + raise exception.ApiNotFound(err_msg) + + return get_api_definition(name, parent_dir_path) + +def load_api_definition(dir_path): + """ load all api definitions in specified dir path + @param (str) dir_path + @return (dict) all api definitions in dir_path merged in one dict + """ + api_files = load_folder_files(dir_path, file_type="api", recursive=False) + + api_def_list = [] + for api_file in api_files: + api_def_list.extend(load_testcases(api_file)) + + api_dir_dict = {} + + for item in api_def_list: + for key in item: + if key == "api": + api_def = item["api"].pop("def") + function_meta = testcase.parse_function(api_def) + func_name = function_meta["func_name"] + + api_info = {} + api_info["function_meta"] = function_meta + api_info.update(item["api"]) + api_dir_dict[func_name] = api_info + + return api_dir_dict + def query_json(json_content, query, delimiter='.'): """ Do an xpath-like query with json_content. @param (json_content) json_content diff --git a/tests/data/api.yml b/tests/data/api.yml new file mode 100644 index 00000000..0143e550 --- /dev/null +++ b/tests/data/api.yml @@ -0,0 +1,26 @@ +- api: + def: get_token($user_name, $device_sn, $os_platform, $app_version) + request: + url: /api/get-token + method: POST + headers: + user_agent: $user_agent + device_sn: $device_sn + os_platform: $os_platform + app_version: $app_version + json: + sign: ${get_sign($user_agent, $device_sn, $os_platform, $app_version)} + validators: + - {"check": "status_code", "comparator": "eq", "expected": 200} + - {"check": "content.token", "comparator": "len_eq", "expected": 16} + +- api: + def: create_user($uid, $user_name, $user_password, $token) + request: + url: /api/users/$uid + method: POST + headers: + token: $token + json: + name: $user_name + password: $user_password diff --git a/tests/data/demo_testset_layer.yml b/tests/data/demo_testset_layer.yml new file mode 100644 index 00000000..0ed32bc3 --- /dev/null +++ b/tests/data/demo_testset_layer.yml @@ -0,0 +1,40 @@ +- config: + name: "create user testsets." + variable_binds: + - user_agent: 'iOS/10.3' + - device_sn: ${gen_random_string(15)} + - os_platform: 'ios' + - app_version: '2.8.6' + request: + base_url: $BASE_URL + headers: + Content-Type: application/json + device_sn: $device_sn + +- test: + name: get token + api: get_token($user_agent, $device_sn, $os_platform, $app_version) + extract_binds: + - token: content.token + +- test: + name: create user which does not exist + variable_binds: + - uid: 1000 + - user_name: "user1" + - user_password: "123456" + api: create_user($uid, $user_name, $user_password, $token) + validators: + - {"check": "status_code", "comparator": "eq", "expected": 201} + - {"check": "content.success", "comparator": "eq", "expected": true} + +- test: + name: create user which does not exist + variable_binds: + - uid: 1000 + - user_name: "user1" + - user_password: "123456" + api: create_user($uid, $user_name, $user_password, $token) + validators: + - {"check": "status_code", "comparator": "eq", "expected": 500} + - {"check": "content.success", "comparator": "eq", "expected": false} diff --git a/tests/test_runner.py b/tests/test_runner.py index c28872c0..58430d65 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -107,3 +107,11 @@ class TestRunner(ApiServerUnittest): results = self.test_runner.run_testsets(testsets) self.assertEqual(len(results), 1) self.assertEqual(results, [[True] * 3]) + + def test_run_testset_layered(self): + testcase_file_path = os.path.join( + os.getcwd(), 'tests/data/demo_testset_layer.yml') + testsets = utils.load_testcases_by_path(testcase_file_path) + results = self.test_runner.run_testsets(testsets) + self.assertEqual(len(results), 1) + self.assertEqual(results, [[True] * 3]) diff --git a/tests/test_utils.py b/tests/test_utils.py index 8496affb..8e330e04 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -44,7 +44,8 @@ class TestUtils(ApiServerUnittest): self.assertNotIn(file1, files) files = utils.load_folder_files(folder, file_type="api", recursive=True) - self.assertEqual(files, []) + api_file = os.path.join(os.getcwd(), 'tests', 'data', 'api.yml') + self.assertEqual(files[0], api_file) def test_load_testcases_by_path_files(self): testsets_list = [] @@ -125,6 +126,36 @@ class TestUtils(ApiServerUnittest): testset_list_3 = utils.load_testcases_by_path(path) self.assertEqual(testset_list_3, []) + def test_load_testcases_by_path_layered(self): + path = os.path.join( + os.getcwd(), 'tests/data/demo_testset_layer.yml') + testsets_list = utils.load_testcases_by_path(path) + self.assertIn("variable_binds", testsets_list[0]["config"]) + self.assertIn("request", testsets_list[0]["config"]) + print(testsets_list[0]["testcases"][0]) + self.assertIn("request", testsets_list[0]["testcases"][0]) + self.assertIn("url", testsets_list[0]["testcases"][0]["request"]) + self.assertIn("validators", testsets_list[0]["testcases"][0]) + + def test_load_api_definition(self): + path = os.path.join( + os.getcwd(), 'tests/data') + api_dir_dict = utils.load_api_definition(path) + self.assertIn("get_token", api_dir_dict) + self.assertEqual("/api/get-token", api_dir_dict["get_token"]["request"]["url"]) + self.assertIn("$user_name", api_dir_dict["get_token"]["function_meta"]["args"]) + self.assertIn("create_user", api_dir_dict) + + def test_get_api_definition(self): + path = os.path.join( + os.getcwd(), 'tests/data') + api_info = utils.get_api_definition("get_token", path) + self.assertEqual("/api/get-token", api_info["request"]["url"]) + self.assertIn(path, utils.api_overall_dict) + + with self.assertRaises(exception.ApiNotFound): + utils.get_api_definition("api_not_exist", path) + def test_query_json(self): json_content = { "ids": [1, 2, 3, 4],