Тестовая среда 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 для тестирования
Что происходит:
- Когда я запускаю свои тесты, окружение перезагружается дважды (об этом свидетельствуют логи).
- При первой инициализации все работает, как и ожидалось, и тест проходит.
- При втором запуске тот же тест не проходит из-за того, что окружение ведет себя не так, как предполагалось.
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'
Шаги для воспроизведения:
- Установите переменные окружения IS_PRODUCTION=False и INJECT_FAKE_DATA=True.
- Запустите набор тестов с помощью pytest с pytest-django.
- Обратите внимание, что тест проходит при первом запуске и проваливается при втором из-за непоследовательного состояния.
Что я пробовал:
- Использование @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 не мешал выполнению тестов? Как я могу правильно управлять этой конфигурацией для тестов?