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метод) в какой-то момент времени, но изменить его для работы должно быть достаточно просто, поскольку вполне вероятно, что в стеке существует какой-то метод, который не существует во время нетестовых миграций.

Другие вопросы по тегам