Django - как обнаружить тестовую среду
Проблема кажется простой, но, к сожалению, гуглить ее немного сложно.
Мой вопрос заключается в следующем: как я могу обнаружить внутри вида, называется ли он в тестовой среде или нет?
#pseudo_code
def my_view(request):
if not request.is_secure() and not TEST_ENVIRONMENT:
return HttpResponseForbidden()
9 ответов
Поместите это в ваши settings.py:
import sys
TESTING = len(sys.argv) > 1 and sys.argv[1] == 'test'
Это проверяет, является ли второй аргумент командной строки (после ./manage.py
) было test
, Затем вы можете получить доступ к этой переменной из других модулей, например так:
from django.conf import settings
if settings.TESTING:
...
Для этого есть веские причины: предположим, у вас есть доступ к некоторому бэкэнд-сервису, кроме моделей Django и соединений с БД. Тогда вам может понадобиться узнать, когда позвонить в производственную службу по сравнению с тестовой службой.
Создайте свой собственный подкласс TestSuiteRunner и измените настройку или сделайте все, что вам нужно для остальной части вашего приложения. Вы указываете тестового бегуна в ваших настройках:
TEST_RUNNER = 'your.project.MyTestSuiteRunner'
В общем, вы не хотите этого делать, но это работает, если вам это абсолютно необходимо.
from django.conf import settings
from django.test.simple import DjangoTestSuiteRunner
class MyTestSuiteRunner(DjangoTestSuiteRunner):
def __init__(self, *args, **kwargs):
settings.IM_IN_TEST_MODE = True
super(MyTestSuiteRunner, self).__init__(*args, **kwargs)
Просто посмотри на request.META['SERVER_NAME']
def my_view(request):
if request.META['SERVER_NAME'] == "testserver":
print "This is test environment!"
Также есть способ временно перезаписать настройки в модульном тесте в Django. Это может быть более простым / чистым решением для определенных случаев.
Вы можете сделать это внутри теста:
with self.settings(MY_SETTING='my_value'):
# test code
Или добавьте его в качестве декоратора для метода испытаний:
@override_settings(MY_SETTING='my_value')
def test_my_test(self):
# test code
Вы также можете установить декоратор для всего класса теста:
@override_settings(MY_SETTING='my_value')
class MyTestCase(TestCase):
# test methods
Для получения дополнительной информации проверьте документы Django: https://docs.djangoproject.com/en/1.11/topics/testing/tools/
Я думаю, что лучший подход - запускать свои тесты, используя собственный файл настроек (т.е. settings/tests.py). Этот файл может выглядеть следующим образом (первая строка импортирует настройки из файла настроек local.py):
from local import *
TEST_MODE = True
Затем выполните ducktyping, чтобы проверить, находитесь ли вы в тестовом режиме.
try:
if settings.TEST_MODE:
print 'foo'
except AttributeError:
pass
Если у вас есть несколько файлов настроек для разных сред, все, что вам нужно сделать, это создать один файл настроек для тестирования.
Например, ваши файлы настроек:
your_project/
|_ settings/
|_ __init__.py
|_ base.py <-- your original settings
|_ testing.py <-- for testing only
В вашем test.py добавьте TESTING
флаг:
from .base import *
TESTING = True
В вашем приложении вы можете получить доступ settings.TESTING
чтобы проверить, находитесь ли вы в тестовой среде.
Для запуска тестов используйте:
python manage.py test --settings your_project.settings.testing
Хотя официального способа узнать, находимся ли мы в тестовой среде, нет, на самом деле django оставляет нам некоторые подсказки. По умолчанию средство запуска тестов Django автоматически перенаправляет все отправленные Django сообщения электронной почты в фиктивный исходящий ящик. Это достигается заменой EMAIL_BACKEND в функции setup_test_environment, которая, в свою очередь, вызывается методом DiscoverRunner. Итак, мы можем проверить, установлен ли settings.EMAIL_BACKEND на 'django.core.mail.backends.locmem.EmailBackend'. Это означает, что мы находимся в тестовой среде.
Менее хакерским решением было бы следовать примеру разработчиков, добавив наши собственные настройки, создав подкласс DisoverRunner, а затем переопределив метод setup_test_environment.
Отвечая на вопрос @Tobia, я думаю, что он лучше реализован в settings.py так:
import sys
try:
TESTING = 'test' == sys.argv[1]
except IndexError:
TESTING = False
Это предотвратит ловлю таких вещей, как ./manage.py loaddata test.json
или же ./manage.py i_am_not_running_a_test
Я хотел исключить некоторые миграции данных из тестов и придумал это решение в проекте Django 3.2:
class Migration(migrations.Migration):
def apply(self, project_state, schema_editor, collect_sql=False):
import inspect
if 'create_test_db' in [i.function for i in inspect.stack()]:
return project_state
else:
return super().apply(project_state, schema_editor, collect_sql=collect_sql)
Я не видел, чтобы это предлагалось в другом месте, и для моих целей это довольно чисто. Конечно, он может сломаться, если Django изменит имя файла.create_test_db
метод (или возвращаемое значениеapply
метод) в какой-то момент времени, но изменить его для работы должно быть достаточно просто, поскольку вполне вероятно, что в стеке существует какой-то метод, который не существует во время нетестовых миграций.