diff --git a/httprunner/loader.py b/httprunner/loader/__init__.py similarity index 85% rename from httprunner/loader.py rename to httprunner/loader/__init__.py index 0bc7a663..3122f0c1 100644 --- a/httprunner/loader.py +++ b/httprunner/loader/__init__.py @@ -4,11 +4,11 @@ import io import json import os import sys -import types import yaml from httprunner import built_in, exceptions, logger, utils +from httprunner.loader.check import is_testcase_path, is_testcases, is_function, validate_json_file try: # PyYAML version >= 5.1 @@ -18,149 +18,6 @@ except AttributeError: pass -# TODO: validate data format with JSON schema - -def is_testcase(data_structure): - """ check if data_structure is a testcase. - - Args: - data_structure (dict): testcase should always be in the following data structure: - - { - "config": { - "name": "desc1", - "variables": [], # optional - "request": {} # optional - }, - "teststeps": [ - test_dict1, - { # test_dict2 - 'name': 'test step desc2', - 'variables': [], # optional - 'extract': [], # optional - 'validate': [], - 'request': {}, - 'function_meta': {} - } - ] - } - - Returns: - bool: True if data_structure is valid testcase, otherwise False. - - """ - # TODO: replace with JSON schema validation - if not isinstance(data_structure, dict): - return False - - if "teststeps" not in data_structure: - return False - - if not isinstance(data_structure["teststeps"], list): - return False - - return True - - -def is_testcases(data_structure): - """ check if data_structure is testcase or testcases list. - - Args: - data_structure (dict): testcase(s) should always be in the following data structure: - { - "project_mapping": { - "PWD": "XXXXX", - "functions": {}, - "env": {} - }, - "testcases": [ - { # testcase data structure - "config": { - "name": "desc1", - "path": "testcase1_path", - "variables": [], # optional - }, - "teststeps": [ - # test data structure - { - 'name': 'test step desc1', - 'variables': [], # optional - 'extract': [], # optional - 'validate': [], - 'request': {} - }, - test_dict_2 # another test dict - ] - }, - testcase_dict_2 # another testcase dict - ] - } - - Returns: - bool: True if data_structure is valid testcase(s), otherwise False. - - """ - if not isinstance(data_structure, dict): - return False - - if "testcases" not in data_structure: - return False - - testcases = data_structure["testcases"] - if not isinstance(testcases, list): - return False - - for item in testcases: - if not is_testcase(item): - return False - - return True - - -def is_testcase_path(path): - """ check if path is testcase path or path list. - - Args: - path (str/list): file path or file path list. - - Returns: - bool: True if path is valid file path or path list, otherwise False. - - """ - if not isinstance(path, (str, list)): - return False - - if isinstance(path, list): - for p in path: - if not is_testcase_path(p): - return False - - if isinstance(path, str): - if not os.path.exists(path): - return False - - return True - - -def validate_json_file(file_list): - """ validate JSON testcase format - """ - for json_file in set(file_list): - if not json_file.endswith(".json"): - logger.log_warning("Only JSON file format can be validated, skip: {}".format(json_file)) - continue - - logger.color_print("Start to validate JSON file: {}".format(json_file), "GREEN") - - with io.open(json_file) as stream: - try: - json.load(stream) - except ValueError as e: - raise SystemExit(e) - - print("OK") - - ############################################################################### # file loader ############################################################################### @@ -388,31 +245,6 @@ def locate_file(start_path, file_name): ############################################################################### -def is_function(item): - """ Takes item object, returns True if it is a function. - """ - return isinstance(item, types.FunctionType) - - -def is_variable(tup): - """ Takes (name, object) tuple, returns True if it is a variable. - """ - name, item = tup - if callable(item): - # function or class - return False - - if isinstance(item, types.ModuleType): - # imported module - return False - - if name.startswith("_"): - # private property - return False - - return True - - def load_module_functions(module): """ load python module functions. diff --git a/httprunner/loader/check.py b/httprunner/loader/check.py new file mode 100644 index 00000000..e386525a --- /dev/null +++ b/httprunner/loader/check.py @@ -0,0 +1,174 @@ +import io +import json +import os +import types + +from httprunner import logger + + +# TODO: validate data format with JSON schema + +def is_testcase(data_structure): + """ check if data_structure is a testcase. + + Args: + data_structure (dict): testcase should always be in the following data structure: + + { + "config": { + "name": "desc1", + "variables": [], # optional + "request": {} # optional + }, + "teststeps": [ + test_dict1, + { # test_dict2 + 'name': 'test step desc2', + 'variables': [], # optional + 'extract': [], # optional + 'validate': [], + 'request': {}, + 'function_meta': {} + } + ] + } + + Returns: + bool: True if data_structure is valid testcase, otherwise False. + + """ + # TODO: replace with JSON schema validation + if not isinstance(data_structure, dict): + return False + + if "teststeps" not in data_structure: + return False + + if not isinstance(data_structure["teststeps"], list): + return False + + return True + + +def is_testcases(data_structure): + """ check if data_structure is testcase or testcases list. + + Args: + data_structure (dict): testcase(s) should always be in the following data structure: + { + "project_mapping": { + "PWD": "XXXXX", + "functions": {}, + "env": {} + }, + "testcases": [ + { # testcase data structure + "config": { + "name": "desc1", + "path": "testcase1_path", + "variables": [], # optional + }, + "teststeps": [ + # test data structure + { + 'name': 'test step desc1', + 'variables': [], # optional + 'extract': [], # optional + 'validate': [], + 'request': {} + }, + test_dict_2 # another test dict + ] + }, + testcase_dict_2 # another testcase dict + ] + } + + Returns: + bool: True if data_structure is valid testcase(s), otherwise False. + + """ + if not isinstance(data_structure, dict): + return False + + if "testcases" not in data_structure: + return False + + testcases = data_structure["testcases"] + if not isinstance(testcases, list): + return False + + for item in testcases: + if not is_testcase(item): + return False + + return True + + +def is_testcase_path(path): + """ check if path is testcase path or path list. + + Args: + path (str/list): file path or file path list. + + Returns: + bool: True if path is valid file path or path list, otherwise False. + + """ + if not isinstance(path, (str, list)): + return False + + if isinstance(path, list): + for p in path: + if not is_testcase_path(p): + return False + + if isinstance(path, str): + if not os.path.exists(path): + return False + + return True + + +def validate_json_file(file_list): + """ validate JSON testcase format + """ + for json_file in set(file_list): + if not json_file.endswith(".json"): + logger.log_warning("Only JSON file format can be validated, skip: {}".format(json_file)) + continue + + logger.color_print("Start to validate JSON file: {}".format(json_file), "GREEN") + + with io.open(json_file) as stream: + try: + json.load(stream) + except ValueError as e: + raise SystemExit(e) + + print("OK") + + +def is_function(item): + """ Takes item object, returns True if it is a function. + """ + return isinstance(item, types.FunctionType) + + +def is_variable(tup): + """ Takes (name, object) tuple, returns True if it is a variable. + """ + name, item = tup + if callable(item): + # function or class + return False + + if isinstance(item, types.ModuleType): + # imported module + return False + + if name.startswith("_"): + # private property + return False + + return True diff --git a/tests/test_loader/__init__.py b/tests/test_loader/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/test_loader/test_check.py b/tests/test_loader/test_check.py new file mode 100644 index 00000000..5ba16571 --- /dev/null +++ b/tests/test_loader/test_check.py @@ -0,0 +1,76 @@ +import unittest + +from httprunner.loader import check + + +class TestLoaderCheck(unittest.TestCase): + + def test_is_function(self): + func = lambda x: x + 1 + self.assertTrue(check.is_function(func)) + self.assertTrue(check.is_function(check.is_testcase)) + + def test_is_testcases(self): + data_structure = "path/to/file" + self.assertFalse(check.is_testcases(data_structure)) + data_structure = ["path/to/file1", "path/to/file2"] + self.assertFalse(check.is_testcases(data_structure)) + + data_structure = { + "project_mapping": { + "PWD": "XXXXX", + "functions": {}, + "env": {} + }, + "testcases": [ + { # testcase data structure + "config": { + "name": "desc1", + "path": "testcase1_path", + "variables": [], # optional + }, + "teststeps": [ + # test data structure + { + 'name': 'test step desc1', + 'variables': [], # optional + 'extract': [], # optional + 'validate': [], + 'request': {} + }, + # test_dict2 # another test dict + ] + }, + # testcase_dict_2 # another testcase dict + ] + } + self.assertTrue(check.is_testcases(data_structure)) + data_structure = [ + { + "name": "desc1", + "config": {}, + "api": {}, + "testcases": ["testcase11", "testcase12"] + }, + { + "name": "desc2", + "config": {}, + "api": {}, + "testcases": ["testcase21", "testcase22"] + } + ] + self.assertTrue(data_structure) + + def test_is_variable(self): + var1 = 123 + var2 = "abc" + self.assertTrue(check.is_variable(("var1", var1))) + self.assertTrue(check.is_variable(("var2", var2))) + + __var = 123 + self.assertFalse(check.is_variable(("__var", __var))) + + func = lambda x: x + 1 + self.assertFalse(check.is_variable(("func", func))) + + self.assertFalse(check.is_variable(("unittest", unittest))) diff --git a/tests/test_loader.py b/tests/test_loader/test_loader.py similarity index 88% rename from tests/test_loader.py rename to tests/test_loader/test_loader.py index 6da7b278..21268074 100644 --- a/tests/test_loader.py +++ b/tests/test_loader/test_loader.py @@ -476,73 +476,3 @@ class TestSuiteLoader(unittest.TestCase): api_file_path = os.path.join(os.getcwd(), "tests", "api", "get_token.yml") self.assertIn(api_file_path, self.tests_def_mapping["api"]) self.assertEqual(self.project_mapping["env"]["PROJECT_KEY"], "ABCDEFGH") - - def test_is_function(self): - func = lambda x: x + 1 - self.assertTrue(loader.is_function(func)) - self.assertTrue(loader.is_function(loader.is_testcase)) - - def test_is_testcases(self): - data_structure = "path/to/file" - self.assertFalse(loader.is_testcases(data_structure)) - data_structure = ["path/to/file1", "path/to/file2"] - self.assertFalse(loader.is_testcases(data_structure)) - - data_structure = { - "project_mapping": { - "PWD": "XXXXX", - "functions": {}, - "env": {} - }, - "testcases": [ - { # testcase data structure - "config": { - "name": "desc1", - "path": "testcase1_path", - "variables": [], # optional - }, - "teststeps": [ - # test data structure - { - 'name': 'test step desc1', - 'variables': [], # optional - 'extract': [], # optional - 'validate': [], - 'request': {} - }, - # test_dict2 # another test dict - ] - }, - # testcase_dict_2 # another testcase dict - ] - } - self.assertTrue(loader.is_testcases(data_structure)) - data_structure = [ - { - "name": "desc1", - "config": {}, - "api": {}, - "testcases": ["testcase11", "testcase12"] - }, - { - "name": "desc2", - "config": {}, - "api": {}, - "testcases": ["testcase21", "testcase22"] - } - ] - self.assertTrue(data_structure) - - def test_is_variable(self): - var1 = 123 - var2 = "abc" - self.assertTrue(loader.is_variable(("var1", var1))) - self.assertTrue(loader.is_variable(("var2", var2))) - - __var = 123 - self.assertFalse(loader.is_variable(("__var", __var))) - - func = lambda x: x + 1 - self.assertFalse(loader.is_variable(("func", func))) - - self.assertFalse(loader.is_variable(("unittest", unittest)))