Как провести юнит-тест с разными настройками в Django? - PullRequest
95 голосов
/ 27 мая 2009

Есть ли какой-нибудь простой механизм переопределения настроек Django для юнит-теста? У меня есть менеджер на одной из моих моделей, который возвращает определенное количество последних объектов. Количество возвращаемых объектов определяется параметром NUM_LATEST.

Это может привести к сбою моих тестов, если кто-то изменит настройку. Как я могу переопределить настройки на setUp() и впоследствии восстановить их на tearDown()? Если это невозможно, могу ли я как-то обезопасить патч или смоделировать настройки?

РЕДАКТИРОВАТЬ: Вот мой код менеджера:

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

Ответы [ 8 ]

142 голосов
/ 20 июня 2011

РЕДАКТИРОВАТЬ: Этот ответ применяется, если вы хотите изменить настройки для малого количества определенных тестов.

Начиная с Django 1.4, во время тестов можно изменить настройки: https://docs.djangoproject.com/en/dev/topics/testing/tools/#overriding-settings

TestCase будет иметь менеджер контекста self.settings, а также будет декоратор @override_settings, который можно применить к методу тестирования или целому подклассу TestCase.

Эти функции еще не существовали в Django 1.3.

Если вы хотите изменить настройки для всех ваших тестов, вам нужно создать отдельный файл настроек для теста, который может загружать и переопределять настройки из вашего основного файла настроек. Есть несколько хороших подходов к этому в других ответах; Я видел успешные варианты подходов hspander и dmitrii .

43 голосов
/ 27 мая 2009

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

20 голосов
/ 19 августа 2010

Обновление : приведенное ниже решение необходимо только для 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()
16 голосов
/ 15 июня 2015

Хотя переопределение настроек конфигурации во время выполнения может помочь, по моему мнению, вам следует создать отдельный файл для тестирования. Это экономит массу конфигурации для тестирования и гарантирует, что вы никогда не будете делать что-то необратимое (например, очистка промежуточной базы данных).

Скажите, что ваш тестовый файл существует в '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

14 голосов
/ 10 июня 2016

Вы можете пройти опцию --settings при запуске тестов

python manage.py test --settings=mysite.settings_local
7 голосов
/ 27 декабря 2016

@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 
3 голосов
/ 14 декабря 2009

Обнаружил это при попытке исправить некоторые doctests ... Для полноты я хочу упомянуть, что если вы собираетесь изменить настройки при использовании doctests, вы должны сделать это перед импортом чего-либо еще ...

>>> from django.conf import settings

>>> settings.SOME_SETTING = 20

>>> # Your other imports
>>> from django.core.paginator import Paginator
>>> # etc
1 голос
/ 10 апреля 2018

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