diff --git a/httprunner/api.py b/httprunner/api.py index c7c8c5c2..76d2eebc 100644 --- a/httprunner/api.py +++ b/httprunner/api.py @@ -36,22 +36,6 @@ class HttpRunner(object): self.kwargs = kwargs self.http_client_session = self.kwargs.pop("http_client_session", None) - self.__loader() - - def __loader(self): - """ load project dependent files, including api/testcase definitions, - environment variables and builtin module. - - """ - loader.reset_loader() - - # load api/testcase definition and debugtalk.py module - project_folder_path = os.path.join(os.getcwd(), "tests") # TODO: remove tests - loader.load_project_tests(project_folder_path) - - self.project_mapping = loader.project_mapping - utils.set_os_environ(self.project_mapping["env"]) - def load_tests(self, path_or_testcases): """ load testcases, extend and merge with api/testcase definitions. @@ -94,14 +78,14 @@ class HttpRunner(object): if isinstance(path_or_testcases, list): for testcase in path_or_testcases: try: - dir_path = os.path.dirname(testcase["config"]["path"]) - loader.load_debugtalk_module(dir_path) + test_path = os.path.dirname(testcase["config"]["path"]) + loader.load_project_tests(test_path) except KeyError: pass else: try: - dir_path = os.path.dirname(path_or_testcases["config"]["path"]) - loader.load_debugtalk_module(dir_path) + test_path = os.path.dirname(path_or_testcases["config"]["path"]) + loader.load_project_tests(test_path) except KeyError: pass @@ -109,6 +93,8 @@ class HttpRunner(object): else: testcases = loader.load_testcases(path_or_testcases) + self.project_mapping = loader.project_mapping + if not testcases: raise exceptions.TestcaseNotFound @@ -245,8 +231,9 @@ class HttpRunner(object): instance: HttpRunner() instance """ - # parser + # loader testcases_list = self.load_tests(path_or_testcases) + # parser parsed_testcases_list = self.parse_tests(testcases_list) # initialize diff --git a/httprunner/loader.py b/httprunner/loader.py index 5681a454..a073d05c 100644 --- a/httprunner/loader.py +++ b/httprunner/loader.py @@ -6,7 +6,7 @@ import json import os import yaml -from httprunner import built_in, exceptions, logger, parser, validator +from httprunner import built_in, exceptions, logger, parser, utils, validator from httprunner.compat import OrderedDict project_mapping = { @@ -192,6 +192,8 @@ def load_dot_env_file(): env_variables_mapping[variable.strip()] = value.strip() project_mapping["env"] = env_variables_mapping + utils.set_os_environ(env_variables_mapping) + return env_variables_mapping @@ -231,6 +233,23 @@ def locate_file(start_path, file_name): return locate_file(os.path.dirname(start_dir_path), file_name) +def locate_pwd(start_path): + """ locate project working directory. + The folder contains debugtalk.py will be used as PWD. + If debugtalk.py is not found, use os.getcwd() as default PWD. + + Args: + start_path (str): start locating path, maybe testcase file path or directory path + + """ + global project_working_directory + try: + debugtalk_path = locate_file(start_path, "debugtalk.py") + project_working_directory = os.path.dirname(debugtalk_path) + except exceptions.FileNotFound: + project_working_directory = os.getcwd() + + ############################################################################### ## debugtalk.py module loader ############################################################################### @@ -297,25 +316,18 @@ def load_builtin_module(): project_mapping["debugtalk"] = built_in_module -def load_debugtalk_module(start_path=None): +def load_debugtalk_module(): """ load project debugtalk.py module and merge with builtin module. - - Args: - start_path (str, optional): start locating path, maybe file path or directory path. - Defaults to current working directory. - - Returns: - dict: variables and functions mapping for debugtalk.py + debugtalk.py should be located in project working directory. + variables and functions mapping for debugtalk.py { "variables": {}, "functions": {} } """ - start_path = start_path or os.getcwd() - try: - module_path = locate_file(start_path, "debugtalk.py") + module_path = locate_file(project_working_directory, "debugtalk.py") module_name = convert_module_name(module_path) except exceptions.FileNotFound: return @@ -734,7 +746,7 @@ def load_folder_content(folder_path): return items_mapping -def load_api_folder(api_folder_path=None): +def load_api_folder(api_folder_path): """ load api definitions from api folder. Args: @@ -775,7 +787,6 @@ def load_api_folder(api_folder_path=None): """ api_definition_mapping = {} - api_folder_path = api_folder_path or os.path.join(os.getcwd(), "api") api_items_mapping = load_folder_content(api_folder_path) for api_file_path, api_items in api_items_mapping.items(): @@ -797,7 +808,7 @@ def load_api_folder(api_folder_path=None): return api_definition_mapping -def load_test_folder(test_folder_path=None): +def load_test_folder(test_folder_path): """ load testcases definitions from folder. Args: @@ -839,8 +850,6 @@ def load_test_folder(test_folder_path=None): """ test_definition_mapping = {} - # TODO: replace suite with testcases - test_folder_path = test_folder_path or os.path.join(os.getcwd(), "suite") test_items_mapping = load_folder_content(test_folder_path) for test_file_path, items in test_items_mapping.items(): @@ -882,6 +891,9 @@ def load_test_folder(test_folder_path=None): def reset_loader(): """ reset project mapping. """ + global project_working_directory + project_working_directory = os.getcwd() + project_mapping["debugtalk"] = { "variables": {}, "functions": {} @@ -892,18 +904,22 @@ def reset_loader(): testcases_cache_mapping.clear() -def load_project_tests(folder_path): - """ load api, testcases and builtin module. +def load_project_tests(test_path): + """ load api, testcases, .env, builtin module and debugtalk.py. + api/testcases folder is relative to project_working_directory Args: - folder_path (str): folder path. + test_path (str): test file/folder path, locate pwd from this path. """ + reset_loader() + locate_pwd(test_path) load_builtin_module() - load_api_folder(os.path.join(folder_path, "api")) - load_test_folder(os.path.join(folder_path, "suite")) + load_api_folder(os.path.join(project_working_directory, "api")) + load_test_folder(os.path.join(project_working_directory, "suite")) # load .env load_dot_env_file() + load_debugtalk_module() def load_testcases(path): @@ -942,14 +958,13 @@ def load_testcases(path): return testcases_cache_mapping[path] if os.path.isdir(path): - load_debugtalk_module(path) + load_project_tests(path) files_list = load_folder_files(path) testcases_list = load_testcases(files_list) elif os.path.isfile(path): try: - dir_path = os.path.dirname(path) - load_debugtalk_module(dir_path) + load_project_tests(path) testcase = _load_test_file(path) if testcase["teststeps"]: testcases_list = [testcase] diff --git a/httprunner/report.py b/httprunner/report.py index 531369e5..607a8422 100644 --- a/httprunner/report.py +++ b/httprunner/report.py @@ -9,7 +9,7 @@ from base64 import b64encode from collections import Iterable from datetime import datetime -from httprunner import logger +from httprunner import loader, logger from httprunner.__about__ import __version__ from httprunner.compat import basestring, bytes, json, numeric_types from jinja2 import Template, escape @@ -95,7 +95,7 @@ def render_html_report(summary, html_report_name=None, html_report_template=None logger.log_info("Start to render Html report ...") logger.log_debug("render data: {}".format(summary)) - report_dir_path = os.path.join(os.getcwd(), "reports") + report_dir_path = os.path.join(loader.project_working_directory, "reports") start_at_timestamp = int(summary["time"]["start_at"]) summary["time"]["start_datetime"] = datetime.fromtimestamp(start_at_timestamp).strftime('%Y-%m-%d %H:%M:%S') if html_report_name: diff --git a/tests/test_api.py b/tests/test_api.py index 03a90722..ddd377dd 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -2,7 +2,7 @@ import os import shutil import time -from httprunner import HttpRunner, LocustRunner +from httprunner import HttpRunner, LocustRunner, loader from locust import HttpLocust from tests.api_server import HTTPBIN_SERVER from tests.base import ApiServerUnittest @@ -89,7 +89,7 @@ class TestHttpRunner(ApiServerUnittest): self.assertEqual(summary["stat"]["skipped"], 4) runner.gen_html_report(html_report_name=output_folder_name) - report_save_dir = os.path.join(os.getcwd(), 'reports', output_folder_name) + report_save_dir = os.path.join(loader.project_working_directory, 'reports', output_folder_name) self.assertGreater(len(os.listdir(report_save_dir)), 0) shutil.rmtree(report_save_dir) @@ -155,7 +155,7 @@ class TestHttpRunner(ApiServerUnittest): output_folder_name = os.path.basename(os.path.splitext(testset_path)[0]) report = runner.gen_html_report(html_report_name=output_folder_name) self.assertTrue(os.path.isfile(report)) - report_save_dir = os.path.join(os.getcwd(), 'reports', output_folder_name) + report_save_dir = os.path.join(loader.project_working_directory, 'reports', output_folder_name) shutil.rmtree(report_save_dir) def test_testcase_layer(self): diff --git a/tests/test_context.py b/tests/test_context.py index f1ce57e5..32da0add 100644 --- a/tests/test_context.py +++ b/tests/test_context.py @@ -9,9 +9,7 @@ from tests.base import ApiServerUnittest class TestContext(ApiServerUnittest): def setUp(self): - project_dir = os.path.join(os.getcwd(), "tests") - loader.load_project_tests(project_dir) - loader.load_debugtalk_module(project_dir) + loader.load_project_tests(os.path.join(os.getcwd(), "tests")) self.debugtalk_module = loader.project_mapping["debugtalk"] self.context = context.Context( diff --git a/tests/test_loader.py b/tests/test_loader.py index 9e5dd401..64430354 100644 --- a/tests/test_loader.py +++ b/tests/test_loader.py @@ -185,15 +185,13 @@ class TestModuleLoader(unittest.TestCase): self.assertNotIn("is_py3", functions_dict) def test_load_debugtalk_module(self): - project_dir = os.path.join(os.getcwd(), "tests") - loader.load_project_tests(project_dir) - loader.load_debugtalk_module() + loader.load_project_tests(os.path.join(os.getcwd(), "httprunner")) imported_module_items = loader.project_mapping["debugtalk"] self.assertIn("equals", imported_module_items["functions"]) self.assertNotIn("SECRET_KEY", imported_module_items["variables"]) self.assertNotIn("alter_response", imported_module_items["functions"]) - loader.load_debugtalk_module("tests") + loader.load_project_tests(os.path.join(os.getcwd(), "tests")) imported_module_items = loader.project_mapping["debugtalk"] self.assertEqual( imported_module_items["variables"]["SECRET_KEY"], @@ -228,13 +226,22 @@ class TestModuleLoader(unittest.TestCase): with self.assertRaises(exceptions.VariableNotFound): loader.get_module_item(module_mapping, "variables", "SECRET_KEY2") + def test_locate_pwd(self): + loader.locate_pwd("tests/data/demo_testcase.yml") + self.assertEqual(loader.project_working_directory, "tests") + + loader.locate_pwd("tests/base.py") + self.assertEqual(loader.project_working_directory, "tests") + + loader.locate_pwd("httprunner/__init__.py") + self.assertEqual(loader.project_working_directory, os.getcwd()) + class TestSuiteLoader(unittest.TestCase): @classmethod def setUpClass(cls): - project_dir = os.path.join(os.getcwd(), "tests") - loader.load_project_tests(project_dir) + loader.load_project_tests(os.path.join(os.getcwd(), "tests")) def test_load_test_file_testcase(self): testcase = loader._load_test_file("tests/testcases/smoketest.yml") @@ -483,9 +490,7 @@ class TestSuiteLoader(unittest.TestCase): ) def test_load_project_tests(self): - loader.project_working_directory = os.path.join(os.getcwd(), "tests") - loader.load_project_tests(loader.project_working_directory) - loader.load_debugtalk_module(loader.project_working_directory) + loader.load_project_tests(os.path.join(os.getcwd(), "tests")) project_mapping = loader.project_mapping self.assertEqual(project_mapping["debugtalk"]["variables"]["SECRET_KEY"], "DebugTalk") self.assertIn("get_token", project_mapping["def-api"]) diff --git a/tests/test_parser.py b/tests/test_parser.py index 42c7ebfa..a5f0de0a 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -412,8 +412,7 @@ class TestParser(unittest.TestCase): ) def test_parse_parameters_mix(self): - project_dir = os.path.join(os.getcwd(), "tests") - loader.load_debugtalk_module(project_dir) + loader.load_project_tests(os.path.join(os.getcwd(), "tests")) project_mapping = loader.project_mapping parameters = [ diff --git a/tests/test_runner.py b/tests/test_runner.py index 1e19c5a0..bc55e979 100644 --- a/tests/test_runner.py +++ b/tests/test_runner.py @@ -10,9 +10,7 @@ from tests.base import ApiServerUnittest class TestRunner(ApiServerUnittest): def setUp(self): - project_dir = os.path.join(os.getcwd(), "tests") - loader.load_project_tests(project_dir) - loader.load_debugtalk_module(project_dir) + loader.load_project_tests(os.path.join(os.getcwd(), "tests")) self.debugtalk_module = loader.project_mapping["debugtalk"] config_dict = { "variables": self.debugtalk_module["variables"],