Django에서 다른 설정으로 단위 테스트하는 방법은 무엇입니까?


116

단위 테스트를 위해 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()단순히 설정이 존재하지 않아야 기본을 제공하는 데 사용됩니다.


@Anto-이유를 설명하거나 더 나은 답변을 제공 할 수 있습니까?
사용자

그 사이에 변했습니다. 이전 허용 하나였다 이 하나 )
ANTO

답변:


163

편집 :이 답변은 적은 수의 특정 테스트에 대한 설정을 변경하려는 경우 적용됩니다 .

Django 1.4부터 테스트 중에 설정을 재정의하는 방법이 있습니다. https://docs.djangoproject.com/en/dev/topics/testing/tools/#overriding-settings

TestCase에는 self.settings 컨텍스트 관리자가 있으며 테스트 메서드 또는 전체 TestCase 하위 클래스에 적용 할 수있는 @override_settings 데코레이터도 있습니다.

이러한 기능은 Django 1.3에 아직 존재하지 않았습니다.

모든 테스트에 대한 설정을 변경하려면 기본 설정 파일에서 설정을로드하고 재정의 할 수있는 별도의 테스트 설정 파일을 만들어야합니다. 다른 답변에는 이에 대한 몇 가지 좋은 접근 방식이 있습니다. 나는 hspanderdmitrii의 접근 방식 모두에서 성공적인 변형을 보았습니다 .


4
Django 1.4+에서이 작업을 수행하는 가장 좋은 방법이라고 말하고 싶습니다
Michael Mior 2013-06-28

나중에 테스트 내에서 해당 설정에 어떻게 액세스합니까? 내가 찾은 최고는와 같은 self.settings().wrapped.MEDIA_ROOT것이지만 꽤 끔찍합니다.
mlissner 2014 년

2
최신 버전의 Django에는이를위한 특정 컨텍스트 관리자가 있습니다. docs.djangoproject.com/en/1.8/topics/testing/tools/…
Akhorus

나의 마음에 드는 : @modify_settings(MIDDLEWARE_CLASSES=...(이 답변에 감사드립니다)
guettli

44

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 값을 수정하는 다른 것이 무엇인지 궁금합니다. 만약 그 "다른 것"이 당신의 테스트 루틴에 의해 촉발된다면, 나는 어떤 양의 원숭이 패치가 테스트 자체의 진실성을 무효화하지 않고 테스트를 저장할 것이라고 확신하지 못합니다.


귀하의 예가 효과적이었습니다. 이것은 단위 테스트의 범위와 테스트 파일의 설정이 호출 스택을 통해 아래로 전파되는 방법 측면에서 눈을 뜨게했습니다.
Soviut

이것은 작동하지 않습니다 settings.TEMPLATE_LOADERS... 그래서 이것은 적어도 일반적인 방법이 아니며 설정이나 Django가 다시로드되지 않거나이 트릭으로 아무것도 아닙니다.
Ciantic

1
이것은 Django 1.4 이전 버전의 좋은 예입니다. > = 1.4 답변 stackoverflow.com/a/6415129/190127 더 정확
Oduvan

사용 docs.djangoproject.com/en/dev/topics/testing/tools/… 이렇게 setUp 및 tearDown으로 패치하는 것은 필요한 것보다 더 장황한 정말 깨지기 쉬운 테스트를 만드는 좋은 방법입니다. 이와 같은 패치가 필요한 경우 flexmock과 같은 것을 사용하십시오.
퍼지 와플

"장고 테스트 케이스는 단일 스레드로 실행되기 때문에": 이는 더 이상 Django 1.9의 경우가 아닙니다.
Wtower 2015

22

런타임에서 설정 구성을 재정의하는 것이 도움이 될 수 있지만 제 생각에는 테스트를 위해 별도의 파일을 만들어야합니다. 이렇게하면 테스트를위한 많은 구성이 절약되며, 이는 복구 할 수없는 작업 (예 : 스테이징 데이터베이스 정리)을 수행하지 않도록합니다.

테스트 파일이 'my_project / test_settings.py'에 있다고 가정하고

settings = 'my_project.test_settings' if 'test' in sys.argv else 'my_project.settings'

manage.py에서. 이렇게하면 실행할 때 python manage.py testtest_settings 만 사용할 수 있습니다. pytest와 같은 다른 테스트 클라이언트를 사용하는 경우 쉽게 pytest.ini에 추가 할 수 있습니다.


2
이것은 나에게 좋은 해결책이라고 생각합니다. 캐시를 사용하는 테스트와 코드가 너무 많습니다. 설정을 하나씩 재정의하는 것은 어려울 것입니다. 두 개의 구성 파일을 만들고 사용할 파일을 결정합니다. MicroPyramid의 답변도 사용할 수 있지만 설정 매개 변수를 한 번 추가하는 것을 잊으면 위험합니다.
ramwin

22

--settings테스트를 실행할 때 옵션 을 전달할 수 있습니다.

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

settings.base의 확장 인 settings.dev에있는 앱을 찾기 위해 중지되었습니다.
holms

4
누군가가 설정 매개 변수를 한 번 추가하는 것을 잊으면 위험하다고 생각합니다.
ramwin

20

업데이트 : 아래 솔루션은 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()

이것은 정말 멋진 솔루션입니다. 어떤 이유로 내 설정이 단위 테스트에서 제대로 작동하지 않았습니다. 공유해 주셔서 감사합니다.
Tomas

이 코드를 사용하고 있지만 문제의 테스트가 실패하면 설정이 되돌릴 수 없기 때문에 계단식 테스트 실패에 문제가있었습니다. 이 문제를 해결하기 yield위해 finally블록에 포함 된 함수의 마지막 부분과 함께 명령문 주위에 try / finally를 추가하여 설정이 항상 되돌려집니다.
Dustin Rasener

후세에 대한 답변을 편집하겠습니다. 이 일을 제대로하고 있기를 바랍니다! :)
Dustin Rasener

11

@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 

여보세요 . Dmitrii, 귀하의 답변에 감사 드립니다이 답변과 동일한 사례가 있지만 앱이 어떻게 알 수 있는지, 우리가있는 환경 (테스트 또는 프로덕션) 에 대한 더 많은 지침을 얻고 싶습니다. 지점을 살펴보고 확인하십시오. 내 repo github.com/andela/ah-backend-iroquois/tree/develop/authors , 어떻게 그 논리를 처리합니까?
Lutaaya Huzaifah Idris

테스트를 실행 하기 위해 nosetest 를 사용하기 때문에 이제 어떻게 실행
됩니까

3

일부 doctest를 수정하는 동안이 문제를 발견했습니다 ... 완전성을 위해 doctest를 사용할 때 설정을 수정하려면 다른 항목을 가져 오기 전에 변경해야한다는 점을 말씀 드리고 싶습니다.

>>> from django.conf import settings

>>> settings.SOME_SETTING = 20

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

3

대한 pytest 사용자.

가장 큰 문제는 다음과 같습니다.

  • override_settings pytest에서는 작동하지 않습니다.
  • Django를 서브 클래 TestCase싱하면 작동하지만 pytest 픽스쳐를 사용할 수 없습니다.

해결책은 여기에settings 설명 된 고정 장치 를 사용하는 입니다.

def test_with_specific_settings(settings):
    settings.DEBUG = False
    settings.MIDDLEWARE = []
    ..

그리고 여러 필드를 업데이트해야하는 경우

def override_settings(settings, kwargs):
    for k, v in kwargs.items():
        setattr(settings, k, v)


new_settings = dict(
    DEBUG=True,
    INSTALLED_APPS=[],
)


def test_with_specific_settings(settings):
    override_settings(settings, new_settings)

3

단일 테스트 기능에 대해서도 설정을 무시할 수 있습니다.

from django.test import TestCase, override_settings

class SomeTestCase(TestCase):

    @override_settings(SOME_SETTING="some_value")
    def test_some_function():
        

또는 클래스의 각 기능에 대한 설정을 재정의 할 수 있습니다.

@override_settings(SOME_SETTING="some_value")
class SomeTestCase(TestCase):

    def test_some_function():
        

1

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

1

다음과 같은 방법으로 테스트의 설정을 재정의 할 수 있습니다.

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.


0

하위 디렉터리 (python 패키지)에 여러 테스트 파일이있는 경우 sys.argv에 'test'문자열이 있는지 조건에 따라 이러한 모든 파일에 대한 설정을 재정의 할 수 있습니다.

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)

최선의 방법은 아닙니다. Celery 브로커를 Redis에서 Memory로 변경하는 데 사용했습니다.


0

나는 settings.py 파일에서 모든 것을 가져오고 테스트 목적으로 다른 것을 수정하는 새로운 settings_test.py 파일을 만들었습니다. 제 경우에는 테스트 할 때 다른 클라우드 스토리지 버킷을 사용하고 싶었습니다. 여기에 이미지 설명 입력

settings_test.py :

from project1.settings import *
import os

CLOUD_STORAGE_BUCKET = 'bucket_name_for_testing'

manage.py :

def main():

    # use seperate settings.py for tests
    if 'test' in sys.argv:
        print('using settings_test.py')
        os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project1.settings_test')
    else:
        os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project1.settings')

    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.