升级Django后,Monorepo中Django测试无法被识别的问题
Django升级后GitHub工作流无法找到测试的问题排查
问题背景
我们采用Monorepo存储多个项目,其中包含一个Django项目,基础目录结构如下:
├── README.md ├── code │ ├── __init__.py │ ├── gunicorn.conf.py │ ├── manage.py │ ├── non-django-project │ │ ├── ... │ ├── django_project │ │ ├── __init__.py │ │ ├── settings.py │ │ ├── apps │ │ │ ├── django_app_1 │ │ │ │ ├── ... │ │ │ │ ├── tests │ │ │ │ │ ├── test_something.py │ │ │ ├── django_app_2 │ │ │ │ ├── ... │ │ │ │ ├── tests │ │ │ │ │ ├── test_something.py │ │ │ ├── django_app_3 │ │ │ │ ├── ... │ │ │ │ ├── tests │ │ │ │ │ ├── test_something.py │ │ │ │ ├── ... │ │ ├── libs │ │ │ ├── lib1.py │ │ │ ├── lib2.py │ │ │ ├── lib1.py │ │ │ ├── tests.py
用于运行Django测试的GitHub工作流配置:
name: django defaults: run: working-directory: ./code on: workflow_dispatch: pull_request: paths: ["code/django_project/**", ".github/workflows/django.yml"] branches: - develop push: branches: - develop jobs: tests: runs-on: ubuntu-20.04 permissions: pull-requests: write contents: write services: redis: image: redis postgres: image: postgres:15.2-alpine env: POSTGRES_PASSWORD: **** POSTGRES_USER: ***** POSTGRES_DB: ****** ports: - 5432:5432 env: PYTHONUNBUFFERED: 1 steps: - name: checkout uses: actions/checkout@v4 - name: Setup python 3.12 uses: actions/setup-python@v4 with: python-version: "3.12" architecture: x64 cache: "pip" cache-dependency-path: code/django_project/requirements.txt - run: sudo apt-get update -qq - name: Install & cache APT Packages uses: awalsh128/cache-apt-pkgs-action@v1 with: packages: build-essential xvfb gdebi version: 1.0 - name: Install locked pip version run: pip install --upgrade pip==23.2.1 - name: Install python dependencies run: pip install -r django_project/requirements.txt - name: Run Django tests run: coverage run --branch --rcfile=.coveragerc ./manage.py test -v 3 --noinput - name: Coverage comment uses: py-cov-action/python-coverage-comment-action@v3 with: COVERAGE_PATH: code/ GITHUB_TOKEN: ${{ github.token }}
settings.py中用于处理路径的代码:
"""settings.py""" # Build paths inside the project like this: join_to_base_dir(BASE_DIR, ...) import os BASE_DIR = os.path.dirname(os.path.abspath(__file__)) def join_to_base_dir(path): return os.path.join(BASE_DIR, path) import sys sys.path.append(join_to_base_dir('.')) sys.path.append(join_to_base_dir('apps'))
报错信息
工作流运行时无法找到测试,报错如下:
Found 1 test(s). Skipping setup of unused database(s): default. System check identified no issues (0 silenced). CODEOWNERS file is not available code.django_project (unittest.loader._FailedTest.code.django_project) ... ERROR ====================================================================== ERROR: code.django_project (unittest.loader._FailedTest.code.django_project) ---------------------------------------------------------------------- ImportError: Failed to import test module: code.django_project Traceback (most recent call last): File "/opt/hostedtoolcache/Python/3.12.0/x64/lib/python3.12/unittest/loader.py", line 415, in _find_test_path package = self._get_module_from_name(name) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/opt/hostedtoolcache/Python/3.12.0/x64/lib/python3.12/unittest/loader.py", line 325, in _get_module_from_name __import__(name) ModuleNotFoundError: No module named 'code.django_project'; 'code' is not a package
该问题在将Django从2.2(Python 3.9)升级到4.2(Python 3.12)后出现,此前运行正常。
问题原因与解决方法
核心原因
Django 4.x的测试加载逻辑比2.x更严格,同时Python 3.12收紧了模块导入规则,之前版本的宽松配置掩盖了路径问题:
- 工作流在
./code目录下运行,Python默认不会将上级目录加入sys.path,导致code无法被识别为Python包 - 测试命令未明确指定测试目标,Django尝试递归查找测试时错误地尝试导入
code.django_project
解决步骤
1. 调整Python路径,将项目根目录加入sys.path
有两种方式可以实现:
- 方法1:在工作流中设置PYTHONPATH
修改"Run Django tests"步骤,提前添加上级目录到Python路径:export PYTHONPATH="$PYTHONPATH:.." && coverage run --branch --rcfile=.coveragerc ./manage.py test -v 3 --noinput - 方法2:修改settings.py的路径配置
更新settings.py中的BASE_DIR和sys.path定义,确保code目录能被识别为包:"""settings.py""" import os import sys # 修改BASE_DIR为code目录(django_project的上级目录) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) def join_to_base_dir(path): return os.path.join(BASE_DIR, path) # 将code目录加入sys.path sys.path.append(BASE_DIR) # 调整apps路径为绝对路径 sys.path.append(join_to_base_dir('django_project/apps'))
2. 明确指定测试目标
避免Django盲目递归查找测试,直接指定要运行的应用测试:
修改工作流中的测试命令,例如:
coverage run --branch --rcfile=.coveragerc ./manage.py test django_project.apps.django_app_1 django_project.apps.django_app_2 django_project.apps.django_app_3 -v 3 --noinput
或者用通配符批量指定所有apps下的测试:
coverage run --branch --rcfile=.coveragerc ./manage.py test django_project.apps.* -v 3 --noinput
3. 确认code/__init__.py的有效性
确保code目录下的__init__.py存在且无语法错误(你的目录结构中已经包含该文件),Python 3.12对包的识别要求更严格,空的__init__.py即可满足需求。
内容的提问来源于stack exchange,提问作者phrfpeixoto
相关产品推荐
相关产品推荐

