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()बस एक डिफ़ॉल्ट सेटिंग मौजूद नहीं होना चाहिए प्रदान करने के लिए प्रयोग किया जाता है।


@ एंटो - क्या आप समझा सकते हैं कि क्यों या एक बेहतर जवाब प्रदान करें?
उपयोगकर्ता

इस बीच यह बदल गया; पूर्व स्वीकार किया गया यह एक था ;)
एंटो

जवाबों:


163

संपादित करें: यह उत्तर लागू होता है यदि आप कम संख्या में विशिष्ट परीक्षणों के लिए सेटिंग्स बदलना चाहते हैं ।

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 के दृष्टिकोण दोनों पर सफल बदलाव देखे हैं ।


4
मैं कहता हूं कि यह अब Django
1.4+

बाद में आप परीक्षण के भीतर से उस सेटिंग तक कैसे पहुँच सकते हैं? सर्वश्रेष्ठ मैंने पाया है कि कुछ ऐसा है self.settings().wrapped.MEDIA_ROOT, लेकिन यह बहुत भयानक है।
mlissner

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 मान को और क्या संशोधित किया जा सकता है? यदि आपकी परीक्षा की दिनचर्या के अनुसार "कुछ और" शुरू हो जाता है, तो मुझे यकीन नहीं है कि बंदर पैचिंग की कोई भी मात्रा परीक्षण की सत्यता को अमान्य किए बिना परीक्षण को बचाएगी।


आपके उदाहरण ने काम किया। यह इकाई परीक्षण के दायरे के संदर्भ में एक आंख खोलने वाला रहा है और परीक्षण फ़ाइल में सेटिंग्स कॉल स्टैक के माध्यम से कैसे फैलती हैं।
सोवियुत

यह साथ काम नहीं करता है settings.TEMPLATE_LOADERS... तो यह कम से कम सामान्य तरीका नहीं है, सेटिंग्स या Django इस चाल के साथ पुनः लोड या कुछ भी नहीं है।

1
यह संस्करण Django पुराने 1.4 के लिए अच्छा उदाहरण है। के लिए> = 1.4 उत्तर stackoverflow.com/a/6415129/190127 और अधिक सही
ओड्वान

Docs.djangoproject.com/en/dev/topics/testing/tools/… को सेटअप और टियरडाउन के साथ पैचिंग का उपयोग करें यह वास्तव में नाजुक परीक्षण करने का एक शानदार तरीका है जो कि होने की तुलना में अधिक क्रियात्मक हैं। यदि आपको कुछ इस तरह से उपयोग करने की आवश्यकता है जैसे फ्लेक्समॉक।
फजी-वफ़ल

"चूंकि django टेस्ट केस सिंगल-थ्रेडेड चलते हैं": जो कि अब Django 1.9 में नहीं है।
Wtower

22

हालाँकि रनटाइम पर सेटिंग्स कॉन्फ़िगरेशन को ओवरराइड करने में मदद मिल सकती है, मेरी राय में आपको परीक्षण के लिए एक अलग फ़ाइल बनानी चाहिए। यह परीक्षण के लिए बहुत सारे कॉन्फ़िगरेशन को बचाता है और यह सुनिश्चित करेगा कि आप कभी भी कुछ अपरिवर्तनीय (जैसे कि स्टेजिंग सफाई डेटाबेस) कर रहे हैं।

कहते हैं कि आपकी परीक्षण फ़ाइल 'my_project / test_settings.py' में मौजूद है, जोड़ें

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

आपके प्रबंध में। यह सुनिश्चित करेगा कि जब आप चलाते हैं python manage.py testतो आप केवल test_settings का उपयोग करते हैं। यदि आप कुछ अन्य परीक्षण क्लाइंट का उपयोग कर रहे हैं जैसे कि pytest, तो आप इसे pytest.ini में आसानी से जोड़ सकते हैं


2
मुझे लगता है कि यह मेरे लिए एक अच्छा समाधान है। मेरे पास बहुत सारे परीक्षण और कोड हैं जो कैश का उपयोग कर रहे हैं। एक-एक करके सेटिंग्स को ओवरराइड करना मेरे लिए मुश्किल होगा। मैं दो कॉन्फिग फ़ाइल बनाऊंगा और यह निर्धारित करूंगा कि किसका उपयोग करना है। MicroPyramid का उत्तर भी avaiable है, लेकिन यह खतरनाक होगा यदि मैं सेटिंग्स मापदंडों को एक बार जोड़ना भूल गया।
ramwin

22

--settingsपरीक्षण चलाते समय आप विकल्प पास कर सकते हैं

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

यह उन ऐप्स को खोजने के लिए बंद हो गया जो सेटिंग में स्थित हैं।
देवा

4
मुझे लगता है कि यह खतरनाक होगा अगर कोई एक बार सेटिंग्स मापदंडों को जोड़ना भूल जाए।
रामविन जु

20

अद्यतन : नीचे दिए गए समाधान की आवश्यकता केवल Django 1.3.x और पहले के संस्करण पर है। > 1.4 के लिए स्लिंकप का उत्तर देखें ।

यदि आप अपने परीक्षणों में बार-बार सेटिंग्स बदलते हैं और पायथन ,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()

यह वास्तव में अच्छा समाधान है। किसी कारण से मेरी सेटिंग्स यूनिट परीक्षणों में ठीक से काम नहीं कर रही थीं। बहुत सुरुचिपूर्ण समाधान, साझा करने के लिए धन्यवाद।
टॉमस

मैं इस कोड का उपयोग कर रहा हूं, लेकिन मुझे कैस्केडिंग टेस्ट विफलताओं की समस्या थी, क्योंकि यदि प्रश्न में परीक्षण विफल हो गया, तो सेटिंग्स वापस नहीं मिलेंगी। इसे संबोधित करने के लिए, मैंने ब्लॉक yieldमें सम्‍मिलित फ़ंक्शन के अंतिम भाग के साथ, कथन के चारों ओर / आखिरकार एक कोशिश की finally, ताकि सेटिंग्स हमेशा उलट हो।
डस्टिन रासनेर

मैं उत्तर देने के लिए उत्तर संपादित करूंगा। मुझे आशा है कि मैं यह सही कर रहा हूँ! :)
डस्टिन रासनेर

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 

हैलो । दिमित्रि, आपके उत्तर के लिए धन्यवाद, इस उत्तर के साथ एक ही मामला है, लेकिन मैं इस बारे में अधिक मार्गदर्शन प्राप्त करना चाहूंगा कि ऐप को कैसे पता चलेगा, हम जिस वातावरण में हैं (परीक्षण या उत्पादन) , मेरी शाखा पर एक नज़र है, देखें मेरे रेपो github.com/andela/ah-backend-iroquois/tree/develop/authors , जैसे कि मैं उस तर्क को कैसे संभालूंगा ?
लुताया हुज़ैफा इदरीस

क्योंकि मैं परीक्षण चलाने के लिए नोसेटेस्ट का उपयोग करता हूं , अब इसे कैसे चलाया जाएगा ?, परीक्षण के माहौल में विकास के माहौल
लुताया हुजैफा इदरीस

3

कुछ सिद्धांतों को ठीक करने की कोशिश करते हुए यह मिला ... पूर्णता के लिए मैं यह उल्लेख करना चाहता हूं कि यदि आप सिद्धांतों का उपयोग करते समय सेटिंग्स को संशोधित करने जा रहे हैं, तो आपको कुछ और आयात करने से पहले इसे करना चाहिए ...

>>> from django.conf import settings

>>> settings.SOME_SETTING = 20

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

3

सबसे आम उपयोगकर्ताओं के लिए।

सबसे बड़ा मुद्दा है:

  • override_settings pytest के साथ काम नहीं करता है।
  • Django की TestCaseसबक्लासिंग इसे काम करेगी लेकिन फिर आप पाइस्टेस्ट जुड़नार का उपयोग नहीं कर सकते।

इसका समाधान यहाँ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

यदि आपके पास एक उपनिर्देशिका (अजगर पैकेज) में कई परीक्षण फाइलें हैं, तो आप 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)

सबसे अच्छा तरीका नहीं। इसका इस्तेमाल सेलेरी ब्रोकर को रेडिस से मेमोरी में बदलने के लिए किया।


0

मैंने एक नई settings_test.py फ़ाइल बनाई, जो सब कुछ settings.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)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.