diff --git a/pythonFiles/testing_tools/unittest_discovery.py b/pythonFiles/testing_tools/unittest_discovery.py index 2988092c387c..bebcf47cffd3 100644 --- a/pythonFiles/testing_tools/unittest_discovery.py +++ b/pythonFiles/testing_tools/unittest_discovery.py @@ -4,11 +4,27 @@ import traceback import unittest +import os.path + +sys.path.insert( + 1, + os.path.dirname( # pythonFiles + os.path.dirname( # pythonFiles/testing_tools + os.path.abspath(__file__) # this file + ) + ), +) + +from unittestadapter.django_test_init import setup_django_test_env + start_dir = sys.argv[1] pattern = sys.argv[2] top_level_dir = sys.argv[3] if len(sys.argv) >= 4 else None sys.path.insert(0, os.getcwd()) +# Setup django env to prevent missing django tests +setup_django_test_env(start_dir) + def get_sourceline(obj): try: diff --git a/pythonFiles/unittestadapter/discovery.py b/pythonFiles/unittestadapter/discovery.py index cbad40ad1838..37ed3cb45dc9 100644 --- a/pythonFiles/unittestadapter/discovery.py +++ b/pythonFiles/unittestadapter/discovery.py @@ -18,6 +18,7 @@ # If I use from utils then there will be an import error in test_discovery.py. from unittestadapter.utils import TestNode, build_test_tree, parse_unittest_args +from unittestadapter.django_test_init import setup_django_test_env from typing_extensions import NotRequired, TypedDict, Literal @@ -113,6 +114,9 @@ def discover_tests( start_dir, pattern, top_level_dir = parse_unittest_args(argv[index + 1 :]) + # Setup django env to prevent missing django tests + setup_django_test_env(start_dir) + # Perform test discovery. port, uuid = parse_discovery_cli_args(argv[:index]) payload = discover_tests(start_dir, pattern, top_level_dir, uuid) diff --git a/pythonFiles/unittestadapter/django_test_init.py b/pythonFiles/unittestadapter/django_test_init.py new file mode 100644 index 000000000000..a7bc834f123c --- /dev/null +++ b/pythonFiles/unittestadapter/django_test_init.py @@ -0,0 +1,55 @@ +""" +This module sets up a Django environment to run Django tests. +""" + +import os +import re +import sys + + +def setup_django_test_env(workspace_directory="."): + """Configures the Django environment for running Django tests. + + If Django is not installed, workspace_directory is not in sys.path or + manage.py can not be found inside the given workspace_directory, the function fails quietly. + + Args: + workspace_directory (str): The current workspace directory that is expected to contain manage.py module + + Returns: + None + """ + + # To avoid false positive ModuleNotFoundError from django.setup() due to missing current workspace in sys.path + sys.path.insert(0, os.getcwd()) + + try: + import django + except ImportError: + return + + manage_py_module = os.path.join(workspace_directory, "manage.py") + if not os.path.exists(manage_py_module): + return + + dj_settings_module = None + + with open(manage_py_module, "r") as manage_py: + pattern = r"^os\.environ\.setdefault\((\'|\")DJANGO_SETTINGS_MODULE(\'|\"), (\'|\")(?P[\w.]+)(\'|\")\)$" + for line in manage_py.readlines(): + match_result = re.match(pattern, line.strip()) + if match_result is not None: + dj_settings_module = match_result.groupdict().get("settings_path", None) + break + + if dj_settings_module is None: + return + + os.environ.setdefault("DJANGO_SETTINGS_MODULE", dj_settings_module) + + try: + django.setup() + except ModuleNotFoundError: + return + + return diff --git a/pythonFiles/unittestadapter/execution.py b/pythonFiles/unittestadapter/execution.py index f239f81c2d87..13c2080196a2 100644 --- a/pythonFiles/unittestadapter/execution.py +++ b/pythonFiles/unittestadapter/execution.py @@ -21,6 +21,7 @@ from testing_tools import process_json_util, socket_manager from unittestadapter.utils import parse_unittest_args +from unittestadapter.django_test_init import setup_django_test_env DEFAULT_PORT = "45454" @@ -259,6 +260,9 @@ def send_run_data(raw_data, port, uuid): start_dir, pattern, top_level_dir = parse_unittest_args(argv[index + 1 :]) + # Setup django env to prevent missing django tests + setup_django_test_env(start_dir) + run_test_ids_port = os.environ.get("RUN_TEST_IDS_PORT") run_test_ids_port_int = ( int(run_test_ids_port) if run_test_ids_port is not None else 0 diff --git a/pythonFiles/visualstudio_py_testlauncher.py b/pythonFiles/visualstudio_py_testlauncher.py index 0b0ef3242f65..411a082d1ff8 100644 --- a/pythonFiles/visualstudio_py_testlauncher.py +++ b/pythonFiles/visualstudio_py_testlauncher.py @@ -25,6 +25,10 @@ import traceback import unittest +sys.path.insert(1, os.path.abspath(__file__)) # this file + +from unittestadapter.django_test_init import setup_django_test_env + try: import thread except: @@ -276,6 +280,9 @@ def main(): ) (opts, _) = parser.parse_args() + # Setup django env to prevent missing django tests + setup_django_test_env(getattr(opts, "us", ".") or ".") + sys.path[0] = os.getcwd() if opts.result_port: try: