Как провести модульный тест с различными настройками в Django?


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

8 89

8 ответов:

изменить: этот ответ применяется, если вы хотите изменить настройки для маленький количество конкретные тесты.

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

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

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

Если вы хотите изменить параметры все ваши тесты, вы хотите создать отдельный файл настроек для теста, который может загружать и переопределять настройки из вашего основного файла настроек. Есть несколько хороших подходов к этому в других ответах; я видел успешные вариации на обоих hspander это и Дмитриевская!--8--> подходы.

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

обновление: Решение ниже необходимо только на Django 1.3.x и ранее. Для >1.4 см. ответ слинка.

Если вы часто меняете настройки в своих тестах и используете 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()

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

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

Вы можете пройти при выполнении тестов

python manage.py test --settings=mysite.settings_local

@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 

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

>>> from django.conf import settings

>>> settings.SOME_SETTING = 20

>>> # Your other imports
>>> from django.core.paginator import Paginator
>>> # etc

Я использую 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