From a92f382571c07031861896e33d21ec0045b7d1ac Mon Sep 17 00:00:00 2001 From: httprunner Date: Tue, 17 Apr 2018 21:41:02 +0800 Subject: [PATCH] call HttpRunner class with testsets data structure other than path --- httprunner/task.py | 70 +++++++++++++++++++++++++++------------- httprunner/testcase.py | 44 ++++++++++++++++++++++++- tests/test_httprunner.py | 63 ++++++++++++++++++++++++++++++++++++ tests/test_task.py | 54 +++++++++++++++++++++++++++++-- tests/test_testcase.py | 29 +++++++++++++++++ 5 files changed, 233 insertions(+), 27 deletions(-) diff --git a/httprunner/task.py b/httprunner/task.py index 314a8bc6..7b9a1f1c 100644 --- a/httprunner/task.py +++ b/httprunner/task.py @@ -152,30 +152,34 @@ class TaskSuite(unittest.TestSuite): """ create task suite with specified testcase path. each task suite may include one or several test suite. """ - def __init__(self, path, mapping=None, http_client_session=None): + def __init__(self, testsets, mapping=None, http_client_session=None): """ @params - path: path could be in several type - - absolute/relative file path - - absolute/relative folder path - - list/set container with file(s) and/or folder(s) - (dict) mapping: + testsets (dict/list): testset or list of testset + testset_dict + or + [ + testset_dict_1, + testset_dict_2, + { + "name": "desc1", + "config": {}, + "api": {}, + "testcases": [testcase11, testcase12] + } + ] + mapping (dict): passed in variables mapping, it will override variables in config block """ super(TaskSuite, self).__init__() mapping = mapping or {} - if not isinstance(path, list): - # absolute/relative file/folder path - path = [path] - - # remove duplicate path - path = set(path) - - testsets = testcase.load_testcases_by_path(path) if not testsets: raise exception.TestcaseNotFound + if isinstance(testsets, dict): + testsets = [testsets] + self.suite_list = [] for testset in testsets: suite = TestSuite(testset, mapping, http_client_session) @@ -187,6 +191,18 @@ class TaskSuite(unittest.TestSuite): return self.suite_list +def init_task_suite(path_or_testsets, mapping=None, http_client_session=None): + """ initialize task suite + """ + if not testcase.is_testsets(path_or_testsets): + testsets = testcase.load_testcases_by_path(path_or_testsets) + else: + testsets = path_or_testsets + + mapping = mapping or {} + return TaskSuite(testsets, mapping, http_client_session) + + class HttpRunner(object): def __init__(self, **kwargs): @@ -198,16 +214,25 @@ class HttpRunner(object): kwargs.setdefault("resultclass", HtmlTestResult) self.runner = unittest.TextTestRunner(**kwargs) - def run(self, path, mapping=None): - """ start to run specified testset file with varaibles mapping - @param (str) path: - YAML/JSON testset file path + def run(self, path_or_testsets, mapping=None): + """ start to run test with varaibles mapping + @param path_or_testsets: YAML/JSON testset file path or testset list + path: path could be in several type + - absolute/relative file path + - absolute/relative folder path + - list/set container with file(s) and/or folder(s) + testsets: testset or list of testset + - (dict) testset_dict + - (list) list of testset_dict + [ + testset_dict_1, + testset_dict_2 + ] @param (dict) mapping: if mapping specified, it will override variables in config block """ try: - mapping = mapping or {} - task_suite = TaskSuite(path, mapping) + task_suite = init_task_suite(path_or_testsets, mapping) except exception.TestcaseNotFound: logger.log_error("Testcases not found in {}".format(path)) sys.exit(1) @@ -238,9 +263,8 @@ class HttpRunner(object): class LocustTask(object): - def __init__(self, path, locust_client, mapping=None): - mapping = mapping or {} - self.task_suite = TaskSuite(path, mapping, locust_client) + def __init__(self, path_or_testsets, locust_client, mapping=None): + self.task_suite = init_task_suite(path_or_testsets, mapping, locust_client) def run(self): for suite in self.task_suite: diff --git a/httprunner/testcase.py b/httprunner/testcase.py index 15bc0ad8..1c1e4c7b 100644 --- a/httprunner/testcase.py +++ b/httprunner/testcase.py @@ -417,6 +417,46 @@ def extend_test_api(test_block_dict): test_extracors ) +def is_testset(data_structure): + """ check if data_structure is a testset + testset should always be in the following data structure: + { + "name": "desc1", + "config": {}, + "api": {}, + "testcases": [testcase11, testcase12] + } + """ + if not isinstance(data_structure, dict): + return False + + if "name" not in data_structure or "testcases" not in data_structure: + return False + + if not isinstance(data_structure["testcases"], list): + return False + + return True + +def is_testsets(data_structure): + """ check if data_structure is testset or testsets + testsets should always be in the following data structure: + testset_dict + or + [ + testset_dict_1, + testset_dict_2 + ] + """ + if not isinstance(data_structure, list): + return is_testset(data_structure) + + for item in data_structure: + if not is_testset(item): + return False + + return True + def load_test_file(file_path): """ load testset file, get testset data structure. @param file_path: absolute valid testset file path @@ -467,7 +507,9 @@ def load_test_file(file_path): testset["api"][func_name] = api_info else: - logger.log_warning("unexpected block: {}. block should only be config or test.".format(key)) + logger.log_warning( + "unexpected block: {}. block should only be 'config', 'test' or 'api'.".format(key) + ) return testset diff --git a/tests/test_httprunner.py b/tests/test_httprunner.py index 77423df0..571ec89e 100644 --- a/tests/test_httprunner.py +++ b/tests/test_httprunner.py @@ -9,6 +9,53 @@ class TestHttpRunner(ApiServerUnittest): def setUp(self): self.testset_path = "tests/data/demo_testset_cli.yml" + self.testset = { + 'name': 'testset description', + 'config': { + 'path': 'docs/data/demo-quickstart-2.yml', + 'name': 'testset description', + 'request': { + 'base_url': '', + 'headers': {'User-Agent': 'python-requests/2.18.4'} + }, + 'variables': [], + 'output': ['token'] + }, + 'api': {}, + 'testcases': [ + { + 'name': '/api/get-token', + 'request': { + 'url': 'http://127.0.0.1:5000/api/get-token', + 'method': 'POST', + 'headers': {'Content-Type': 'application/json', 'app_version': '2.8.6', 'device_sn': 'FwgRiO7CNA50DSU', 'os_platform': 'ios', 'user_agent': 'iOS/10.3'}, + 'json': {'sign': '958a05393efef0ac7c0fb80a7eac45e24fd40c27'} + }, + 'extract': [ + {'token': 'content.token'} + ], + 'validate': [ + {'eq': ['status_code', 200]}, + {'eq': ['headers.Content-Type', 'application/json']}, + {'eq': ['content.success', True]} + ] + }, + { + 'name': '/api/users/1000', + 'request': { + 'url': 'http://127.0.0.1:5000/api/users/1000', + 'method': 'POST', + 'headers': {'Content-Type': 'application/json', 'device_sn': 'FwgRiO7CNA50DSU','token': '$token'}, 'json': {'name': 'user1', 'password': '123456'} + }, + 'validate': [ + {'eq': ['status_code', 201]}, + {'eq': ['headers.Content-Type', 'application/json']}, + {'eq': ['content.success', True]}, + {'eq': ['content.msg', 'user created successfully.']} + ] + } + ] + } self.reset_all() def reset_all(self): @@ -35,3 +82,19 @@ class TestHttpRunner(ApiServerUnittest): runner.gen_html_report(html_report_name=output_folder_name) report_save_dir = os.path.join(os.getcwd(), 'reports', output_folder_name) shutil.rmtree(report_save_dir) + + def test_run_testsets(self): + testsets = [self.testset] + runner = HttpRunner().run(testsets) + summary = runner.summary + self.assertTrue(summary["success"]) + self.assertEqual(summary["stat"]["testsRun"], 2) + self.assertIn("records", summary) + + def test_run_testset(self): + testsets = self.testset + runner = HttpRunner().run(testsets) + summary = runner.summary + self.assertTrue(summary["success"]) + self.assertEqual(summary["stat"]["testsRun"], 2) + self.assertIn("records", summary) diff --git a/tests/test_task.py b/tests/test_task.py index 4b1f5804..8fa3614f 100644 --- a/tests/test_task.py +++ b/tests/test_task.py @@ -24,9 +24,57 @@ class TestTask(ApiServerUnittest): self.assertIsInstance(testcase, task.TestCase) def test_create_task(self): - testcase_file_path = os.path.join(os.getcwd(), 'tests/data/demo_testset_variables.yml') - task_suite = task.TaskSuite(testcase_file_path) - self.assertEqual(task_suite.countTestCases(), 3) + testsets = [ + { + 'name': 'testset description', + 'config': { + 'path': 'docs/data/demo-quickstart-2.yml', + 'name': 'testset description', + 'request': { + 'base_url': '', + 'headers': {'User-Agent': 'python-requests/2.18.4'} + }, + 'variables': [], + 'output': ['token'] + }, + 'api': {}, + 'testcases': [ + { + 'name': '/api/get-token', + 'request': { + 'url': 'http://127.0.0.1:5000/api/get-token', + 'method': 'POST', + 'headers': {'Content-Type': 'application/json', 'app_version': '2.8.6', 'device_sn': 'FwgRiO7CNA50DSU', 'os_platform': 'ios', 'user_agent': 'iOS/10.3'}, + 'json': {'sign': '958a05393efef0ac7c0fb80a7eac45e24fd40c27'} + }, + 'extract': [ + {'token': 'content.token'} + ], + 'validate': [ + {'eq': ['status_code', 200]}, + {'eq': ['headers.Content-Type', 'application/json']}, + {'eq': ['content.success', True]} + ] + }, + { + 'name': '/api/users/1000', + 'request': { + 'url': 'http://127.0.0.1:5000/api/users/1000', + 'method': 'POST', + 'headers': {'Content-Type': 'application/json', 'device_sn': 'FwgRiO7CNA50DSU','token': '$token'}, 'json': {'name': 'user1', 'password': '123456'} + }, + 'validate': [ + {'eq': ['status_code', 201]}, + {'eq': ['headers.Content-Type', 'application/json']}, + {'eq': ['content.success', True]}, + {'eq': ['content.msg', 'user created successfully.']} + ] + } + ] + } + ] + task_suite = task.TaskSuite(testsets) + self.assertEqual(task_suite.countTestCases(), 2) for suite in task_suite: for testcase in suite: self.assertIsInstance(testcase, task.TestCase) diff --git a/tests/test_testcase.py b/tests/test_testcase.py index 4d1518f3..86d5909b 100644 --- a/tests/test_testcase.py +++ b/tests/test_testcase.py @@ -738,3 +738,32 @@ class TestcaseParserUnittest(unittest.TestCase): {"var3": "val3"}, merged_extractors ) + + def test_is_testsets(self): + data_structure = "path/to/file" + self.assertFalse(testcase.is_testsets(data_structure)) + data_structure = ["path/to/file1", "path/to/file2"] + self.assertFalse(testcase.is_testsets(data_structure)) + + data_structure = { + "name": "desc1", + "config": {}, + "api": {}, + "testcases": ["testcase11", "testcase12"] + } + self.assertTrue(data_structure) + data_structure = [ + { + "name": "desc1", + "config": {}, + "api": {}, + "testcases": ["testcase11", "testcase12"] + }, + { + "name": "desc2", + "config": {}, + "api": {}, + "testcases": ["testcase21", "testcase22"] + } + ] + self.assertTrue(data_structure)