Тестовая среда Django запускается дважды и терпит неудачу, если не использовать IS_PRODUCTION и INJECT_FAKE_DATA

Описание:

Я столкнулся со странной проблемой в моем тестовом окружении Django. При запуске моего тестового набора окружение инициализируется дважды, в результате чего определенный тест проходит в первый раз и терпит неудачу при втором запуске. Похоже, что проблема связана со следующим условным оператором в моем файле settings.py:

if not IS_PRODUCTION and INJECT_FAKE_DATA:
    # Code for injecting fake data

Сведения об окружающей среде:

  • Django версия: 5.1.1
  • Python версия: 3.12.1
  • Фреймворк тестирования: pytest с pytest-django
  • База данных: MongoDB для тестирования

Что происходит:

  1. Когда я запускаю свои тесты, окружение перезагружается дважды (об этом свидетельствуют логи).
  2. При первой инициализации все работает, как и ожидалось, и тест проходит.
  3. При втором запуске тот же тест не проходит из-за того, что окружение ведет себя не так, как предполагалось.
INFO:C:\path\to\settings.py changed, reloading.
INFO:INJECT_FAKE_DATA is True.
INFO:Fake data mode activated.
INFO:Watching for file changes with StatReloader
...
Performing system checks...
System check identified no issues (0 silenced).
...
First run: test passes.
Second run: test fails with inconsistent state.

и

PS C:\Users\Chevr\OneDrive\Bureau\eOnsight\eOnsightApi\eonsight-api> pytest app_auth
============================================================= test session starts ==============================================================
platform win32 -- Python 3.12.1, pytest-8.3.3, pluggy-1.5.0
django: version: 5.1.1, settings: eOnsight_api.settings (from ini)
rootdir: C:\Users\Chevr\OneDrive\Bureau\eOnsight\eOnsightApi\eonsight-api
configfile: pytest.ini
plugins: Faker-30.0.0, django-4.9.0, factoryboy-2.7.0, mongo-3.1.0
collected 2 items

app_auth/tests/test_auth_viewset.py::test_login PASSED                                                                                    [ 50%]
app_auth/tests/test_auth_viewset.py::test_login ERROR                                                                                     [ 50%]
app_auth/tests/test_auth_viewset.py::test_logout PASSED                                                                                   [100%]
app_auth/tests/test_auth_viewset.py::test_logout ERROR                                                                                    [100%] 

==================================================================== ERRORS ==================================================================== 
_______________________________________________________ ERROR at teardown of test_login ________________________________________________________ 

code in setting.py:

один из моих тестовых app_auth:

import pytest
from django.urls import reverse
from rest_framework.test import APIClient
from rest_framework import status
from app_users.models import User
from app_users.service import UserService

@pytest.fixture
def api_client():
    return APIClient()

@pytest.fixture
def create_user(mongo_db):
    """
    Fixture pour créer des utilisateurs dans la base MongoDB.
    """
    def _create_user(email=None, password="password123", role="user"):
        if not email:
            email = f"user{User.objects.count()}@example.com" 
        return UserService.create_user(email=email, password=password, role=role)
    return _create_user

@pytest.mark.django_db
def test_login(api_client, create_user):
    """
    Test for user login and JWT token retrieval.
    """
    user = create_user(email='testuser@example.com', password='password123')
    payload = {
        'email': user.email,
        'password': 'password123'
    }
    response = api_client.post(reverse('token_obtain_pair'), payload, format='json')
    assert response.status_code == status.HTTP_200_OK
    assert 'access' in response.data
    assert 'refresh' in response.data

@pytest.mark.django_db
def test_logout(api_client, create_user):
    """
    Test for user logout using both access and refresh tokens.
    """
    user = create_user(email='testuser@example.com', password='password123')
    
    login_payload = {
        'email': user.email,
        'password': 'password123'
    }

    login_response = api_client.post(reverse('token_obtain_pair'), login_payload, format='json')

    assert login_response.status_code == status.HTTP_200_OK

    access_token = login_response.data['access']
    refresh_token = login_response.data['refresh']

    api_client.credentials(HTTP_AUTHORIZATION=f'Bearer {access_token}')
    
    print(f'user access token: {access_token}')
    print(f'user refresh token: {refresh_token}')

    logout_payload = {
        'refresh_token': refresh_token
    }

    logout_response = api_client.post(reverse('logout'), logout_payload, format='json')

    assert logout_response.status_code == status.HTTP_205_RESET_CONTENT
    assert logout_response.data['success'] == 'Tokens have been invalidated successfully'

Шаги для воспроизведения:

  1. Установите переменные окружения IS_PRODUCTION=False и INJECT_FAKE_DATA=True.
  2. Запустите набор тестов с помощью pytest с pytest-django.
  3. Обратите внимание, что тест проходит при первом запуске и проваливается при втором из-за непоследовательного состояния.

Что я пробовал:

  • Использование @override_settings в тестах, чтобы убедиться, что IS_PRODUCTION и INJECT_FAKE_DATA установлены правильно.
  • Заставить IS_PRODUCTION и INJECT_FAKE_DATA быть явными булевыми значениями в settings.py.
  • Отключение наблюдателя за файлами (--noreload) во время выполнения теста.
  • Запись в журнал значений IS_PRODUCTION и INJECT_FAKE_DATA, чтобы убедиться, что они установлены правильно.
  • Ошибка исчезает при сравнении IS_PRODUCTION и INJECT_FAKE_DATA как строк, как это, но это не является последовательным решением:
if IS_PRODUCTION == 'false' and INJECT_FAKE_DATA == 'true':
    # Code here

Ожидаемое поведение: Среда должна инициализироваться только один раз за прогон теста, а условие в settings.py не должно приводить к перезагрузке среды.

Фактическое поведение: Среда перезагружается дважды, и вторая инициализация вызывает непоследовательное поведение, что приводит к сбоям в тестировании.

Вопрос: Что может привести к тому, что тестовое окружение Django инициализируется дважды, и как сделать так, чтобы условный блок в settings.py не мешал выполнению тестов? Как я могу правильно управлять этой конфигурацией для тестов?

Вернуться на верх