Как Unit test с разными настройками в Django?
Есть ли простой механизм для переопределения настроек Django для unit test? У меня есть менеджер на одной из моих моделей, которая возвращает определенное количество последних объектов. Количество возвращаемых объектов определяется параметром NUM_LATEST.
Это может привести к сбою моих тестов, если кто-то изменит настройку. Как я могу переопределить настройки на setUp()
и впоследствии восстановить их на tearDown()
? Если это невозможно, можно ли каким-то образом обезвредить метод или издеваться над настройками?
EDIT: Вот мой код менеджера:
class LatestManager(models.Manager):
"""
Returns a specific number of the most recent public Articles as defined by
the NEWS_LATEST_MAX setting.
"""
def get_query_set(self):
num_latest = getattr(settings, 'NEWS_NUM_LATEST', 10)
return super(LatestManager, self).get_query_set().filter(is_public=True)[:num_latest]
Менеджер использует settings.NEWS_LATEST_MAX
для среза запроса. getattr()
просто используется для предоставления значения по умолчанию, если параметр не существует.
Ответы
Ответ 1
EDIT: этот ответ применяется, если вы хотите изменить настройки для малого числа конкретных тестов.
Начиная с Django 1.4, есть способы переопределить настройки во время тестов:
https://docs.djangoproject.com/en/dev/topics/testing/tools/#overriding-settings
TestCase будет иметь диспетчер контекстов self.settings, а также будет декоратор @override_settings, который может быть применен либо к тестовому методу, либо к подклассу TestCase.
Эти функции еще не существовали в Django 1.3.
Если вы хотите изменить настройки для all своих тестов, вы захотите создать отдельный файл настроек для теста, который может загружать и переопределять настройки из файла основных настроек. В других ответах есть несколько хороших подходов; Я видел успешные варианты на hspander и dmitrii.
Ответ 2
Вы можете делать все, что вам нравится в подклассе UnitTest
, включая свойства экземпляра и чтения экземпляра:
from django.conf import settings
class MyTest(unittest.TestCase):
def setUp(self):
self.old_setting = settings.NUM_LATEST
settings.NUM_LATEST = 5 # value tested against in the TestCase
def tearDown(self):
settings.NUM_LATEST = self.old_setting
Так как тестовые примеры django работают с однопоточными, мне любопытно, что еще может изменить значение NUM_LATEST? Если это "что-то другое" вызвано вашей тестовой процедурой, я не уверен, что любое количество исправлений обезьяны сохранит тест, не нарушая достоверности самих тестов.
Ответ 3
Обновление: нижеприведенное решение требуется только для Django 1.3.x и более ранних версий. Для > 1.4 см. ответ slinkp.
Если вы часто меняете настройки в своих тестах и используете Python ≥2.5, это также удобно:
from contextlib import contextmanager
class SettingDoesNotExist:
pass
@contextmanager
def patch_settings(**kwargs):
from django.conf import settings
old_settings = []
for key, new_value in kwargs.items():
old_value = getattr(settings, key, SettingDoesNotExist)
old_settings.append((key, old_value))
setattr(settings, key, new_value)
yield
for key, old_value in old_settings:
if old_value is SettingDoesNotExist:
delattr(settings, key)
else:
setattr(settings, key, old_value)
Затем вы можете сделать:
with patch_settings(MY_SETTING='my value', OTHER_SETTING='other value'):
do_my_tests()
Ответ 4
Вы можете передать опцию --settings
при запуске тестов
python manage.py test --settings=mysite.settings_local
Ответ 5
Хотя переопределение настроек конфигурации во время выполнения может помочь, на мой взгляд, вы должны создать отдельный файл для тестирования. Это позволяет сэкономить массу настроек для тестирования, и это гарантирует, что вы никогда не будете делать что-то необратимое (например, очистить промежуточную базу данных).
Скажите, что ваш тестовый файл существует в 'my_project/test_settings.py', добавьте
settings = 'my_project.test_settings' if 'test' in sys.argv else 'my_project.settings'
в файле manage.py. Это гарантирует, что при запуске python manage.py test
вы используете только test_settings. Если вы используете какой-либо другой клиент для тестирования, такой как pytest, вы можете легко добавить его в pytest.ini
Ответ 6
@override_settings
отлично, если у вас не так много различий между конфигурациями вашей продукции и тестирования.
В другом случае вам лучше иметь разные файлы настроек. В этом случае ваш проект будет выглядеть следующим образом:
your_project
your_app
...
settings
__init__.py
base.py
dev.py
test.py
production.py
manage.py
Таким образом, вам нужно иметь максимальную настройку в base.py
, а затем в других файлах вам нужно будет импортировать все, что угодно, и переопределить некоторые параметры. Здесь будет выглядеть ваш файл test.py
:
from .base import *
DEBUG = False
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': 'app_db_test'
}
}
PASSWORD_HASHERS = (
'django.contrib.auth.hashers.MD5PasswordHasher',
)
LOGGING = {}
И тогда вам либо нужно указать опцию --settings
, как в ответе @MicroPyramid, либо указать переменную среды DJANGO_SETTINGS_MODULE
, а затем вы можете запустить свои тесты:
export DJANGO_SETTINGS_MODULE=settings.test
python manage.py test
Ответ 7
Нашел это, пытаясь исправить некоторые доктрины... Для полноты я хочу упомянуть, что если вы собираетесь изменять настройки при использовании доктрин, вы должны сделать это, прежде чем импортировать что-нибудь еще...
>>> from django.conf import settings
>>> settings.SOME_SETTING = 20
>>> # Your other imports
>>> from django.core.paginator import Paginator
>>> # etc
Ответ 8
Я использую pytest.
Мне удалось решить это следующим образом:
import django
import app.setting
import modules.that.use.setting
# do some stuff with default setting
setting.VALUE = "some value"
django.setup()
import importlib
importlib.reload(app.settings)
importlib.reload(modules.that.use.setting)
# do some stuff with settings new value
Ответ 9
Вы можете изменить настройки в тесте следующим образом:
from django.test import TestCase, override_settings
test_settings = override_settings(
DEFAULT_FILE_STORAGE='django.core.files.storage.FileSystemStorage',
PASSWORD_HASHERS=(
'django.contrib.auth.hashers.UnsaltedMD5PasswordHasher',
)
)
@test_settings
class SomeTestCase(TestCase):
"""Your test cases in this class"""
И если вам нужны те же настройки в другом файле, вы можете просто импортировать test_settings
.
Ответ 10
Если у вас есть несколько тестовых файлов, размещенных в подкаталоге (пакет python), вы можете переопределить настройки для всех этих файлов в зависимости от наличия строки 'test' в sys.argv
app
tests
__init__.py
test_forms.py
test_models.py
__init__.py:
import sys
from project import settings
if 'test' in sys.argv:
NEW_SETTINGS = {
'setting_name': value,
'another_setting_name': another_value
}
settings.__dict__.update(NEW_SETTINGS)
Не лучший подход. Используйте его, чтобы изменить брокера из сельдерея с Redis на Memory.