Django'da farklı ayarlarla birim testi nasıl yapılır?


117

Bir birim testi için Django ayarlarını geçersiz kılmak için herhangi bir basit mekanizma var mı? Modellerimden birinde belirli sayıda en son nesneyi döndüren bir yöneticim var. Döndürdüğü nesnelerin sayısı NUM_LATEST ayarı ile tanımlanır.

Birisinin ayarı değiştirmesi durumunda bu, testlerimin başarısız olmasına neden olabilir. Ayarları üzerindeki ayarları nasıl geçersiz kılabilir setUp()ve daha sonra nasıl geri yükleyebilirim tearDown()? Bu mümkün değilse, yöntemi yamalamanın veya ayarlarla dalga geçmenin bir yolu var mı?

DÜZENLEME: İşte yönetici kodum:

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]

Yönetici settings.NEWS_LATEST_MAX, sorgu kümesini dilimlemek için kullanır . getattr()Basitçe ayarı var olmamalıdır bir varsayılan sağlamak için kullanılır.


@Anto - nedenini açıklayabilir veya daha iyi bir cevap verebilir misiniz?
kullanıcı

Bu arada değişti; Eski kabul edilen biriydi bu bir ;)
Anto

Yanıtlar:


164

DÜZENLEME: Bu yanıt, az sayıda belirli test için ayarları değiştirmek isterseniz geçerlidir .

Django 1.4'ten beri, testler sırasında ayarları geçersiz kılmanın yolları vardır: https://docs.djangoproject.com/en/dev/topics/testing/tools/#overriding-settings

TestCase bir self.settings bağlam yöneticisine sahip olacak ve ayrıca bir test yöntemine veya tüm TestCase alt sınıfına uygulanabilecek bir @override_settings dekoratörü olacaktır.

Bu özellikler henüz Django 1.3'te mevcut değildi.

Tüm testleriniz için ayarları değiştirmek isterseniz, test için ana ayarlar dosyanızdan ayarları yükleyebilen ve geçersiz kılan ayrı bir ayarlar dosyası oluşturmak isteyeceksiniz. Diğer cevaplarda buna birkaç iyi yaklaşım var; Hem başarılı varyasyonları gördük hspander yılların ve dmitrii en yaklaşımlar.


4
Bunu Django 1.4+ ile yapmanın en iyi yolu olduğunu söyleyebilirim
Michael Mior

Bu ayara daha sonra testler içinden nasıl erişirsiniz? Bulduğum en iyi şey şöyle bir şey self.settings().wrapped.MEDIA_ROOT, ama bu oldukça korkunç.
mlissner

2
Django'nun daha yeni sürümlerinde bunun için belirli bir bağlam yöneticisi var: docs.djangoproject.com/en/1.8/topics/testing/tools/…
Akhorus

En sevdiğim: @modify_settings(MIDDLEWARE_CLASSES=...(bu cevap için teşekkür ederim)
guettli

44

UnitTestÖrnek özelliklerini ayarlamak ve okumak dahil olmak üzere alt sınıfa istediğiniz her şeyi yapabilirsiniz :

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 test senaryoları tek iş parçacıklı çalıştığından, NUM_LATEST değerini başka nelerin değiştirebileceğini merak ediyorum? Eğer bu "başka bir şey" test rutininiz tarafından tetiklenirse, o zaman herhangi bir maymun yaması uygulamasının testin doğruluğunu geçersiz kılmaksızın testi kurtaracağından emin değilim.


Örneğiniz işe yaradı. Bu, birim testinin kapsamı ve test dosyasındaki ayarların çağrı yığınında nasıl yayıldığı açısından bir göz açıcı olmuştur.
Soviut

Bu işe yaramıyor settings.TEMPLATE_LOADERS... Yani bu en azından genel bir yol değil, ayarlar veya Django yeniden yüklenmedi veya bu numara ile herhangi bir şey yapılmadı.
Ciantic

1
bu, 1.4 ve üstü Django sürümü için iyi bir örnektir. > = 1.4 yanıtı için stackoverflow.com/a/6415129/190127 daha doğru
Oduvan

Docs.djangoproject.com/en/dev/topics/testing/tools/… 'u kullanın. SetUp ve tearDown ile yama yapmak, olması gerekenden daha ayrıntılı olan gerçekten hassas testler yapmanın harika bir yoludur. Böyle bir şeye yama yapmanız gerekirse flexmock gibi bir şey kullanın.
fuzzy-waffle

"Django test durumları tek iş parçacıklı çalıştığından": artık Django 1.9'da durum böyle değil.
Wtower

22

Çalışma zamanında ayar yapılandırmasını geçersiz kılmak yardımcı olabilir, ancak bence test için ayrı bir dosya oluşturmalısınız. Bu, test için çok fazla yapılandırma tasarrufu sağlar ve bu, asla geri döndürülemez bir şey yapmamanızı sağlar (aşama veritabanını temizleme gibi).

Test dosyanızın 'projem / test_settings.py'de olduğunu varsayalım, ekle

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

manage.py dosyanızda. Bu, çalıştırdığınızda python manage.py testyalnızca test_settings kullanmanızı sağlayacaktır. Pytest gibi başka bir test istemcisi kullanıyorsanız, bunu kolayca pytest.ini'ye ekleyebilirsiniz.


2
Bunun benim için iyi bir çözüm olduğunu düşünüyorum. Önbellek kullanan çok fazla testim ve kodum var. Ayarları tek tek geçersiz kılmak benim için zor olacak. İki yapılandırma dosyası oluşturacağım ve hangisinin kullanılacağını belirleyeceğim. MicroPyramid'in cevabı da mevcuttur, ancak ayar parametrelerini bir kez eklemeyi unutursam tehlikeli olur.
ramwin

22

--settingsTest çalıştırırken seçeneği geçebilirsiniz

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

settings.base'in uzantısı olan settings.dev'de bulunan uygulamaları bulmayı durdurdu
holms

4
Birinin ayar parametrelerini bir kez eklemeyi unutmasının tehlikeli olacağını düşünüyorum.
ramwin

20

Güncelleme : Aşağıdaki çözüm yalnızca Django 1.3.x ve öncesinde gereklidir. > 1.4 için slinkp'in cevabına bakın .

Testlerinizde ayarları sık sık değiştirirseniz ve Python ≥2.5 kullanıyorsanız, bu da kullanışlıdır:

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)

O zaman şunları yapabilirsiniz:

with patch_settings(MY_SETTING='my value', OTHER_SETTING='other value'):
    do_my_tests()

Bu gerçekten harika bir çözüm. Bazı nedenlerden dolayı, ayarlarım birim testlerinde düzgün çalışmıyordu. Çok zarif çözüm, paylaştığınız için teşekkürler.
Tomas

Bu kodu kullanıyorum, ancak basamaklı test hatalarıyla ilgili sorunlar yaşadım çünkü söz konusu test başarısız olursa ayarlar geri alınmayacaktı. Bunu ele almak için yield, finallyblok içinde bulunan fonksiyonun son kısmı ile ifadenin etrafına bir dene / nihayet ekledim , böylece ayarlar her zaman geri alınır.
Dustin Rasener

Cevabı gelecek nesil için düzenleyeceğim. Umarım bunu doğru yapıyorum! :)
Dustin Rasener

11

@override_settings üretim ve test ortamı yapılandırmalarınız arasında çok fazla fark yoksa harika.

Diğer durumda, farklı ayar dosyalarına sahip olsanız iyi olur. Bu durumda projeniz şöyle görünecektir:

your_project
    your_app
        ...
    settings
        __init__.py
        base.py
        dev.py
        test.py
        production.py
    manage.py

Bu nedenle, ayarlarınızın çoğunu base.pydiğer dosyalarda ve ardından diğer dosyalarda bulundurmanız gerekir, buradan her şeyi içeri aktarmanız ve bazı seçenekleri geçersiz kılmanız gerekir. İşte ne test.pydosya gibi görünecektir:

from .base import *

DEBUG = False

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': 'app_db_test'
    }
}

PASSWORD_HASHERS = (
    'django.contrib.auth.hashers.MD5PasswordHasher',
)

LOGGING = {}

Ve sonra ya --settings@MicroPyramid cevabındaki gibi bir seçenek belirtmeniz ya da DJANGO_SETTINGS_MODULEortam değişkenini belirtmeniz gerekir ve ardından testlerinizi çalıştırabilirsiniz:

export DJANGO_SETTINGS_MODULE=settings.test
python manage.py test 

Merhaba . Dmitrii, cevabınız için teşekkürler, bu cevapla aynı durumu yaşıyorum, ancak uygulamanın, içinde bulunduğumuz ortamı (test veya üretim) nasıl bileceği konusunda daha fazla rehberlik almak istiyorum , şubeme bir göz atın, kontrol edin benim repo github.com/andela/ah-backend-iroquois/tree/develop/authors ben bu mantığı nasıl işleyeceğini gibi?
Lutaaya Huzaifah Idris

Testleri çalıştırmak için nosetest kullandığım için, şimdi bu nasıl çalıştırılacak ?, geliştirme ortamında
Lutaaya Huzaifah Idris

3

Bunu bazı doctestleri düzeltmeye çalışırken buldum ... Tamlık için, doctestleri kullanırken ayarları değiştirecekseniz, başka herhangi bir şeyi içe aktarmadan önce yapmanız gerektiğini belirtmek isterim ...

>>> from django.conf import settings

>>> settings.SOME_SETTING = 20

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

3

İçin pytest kullanıcılar.

En büyük sorun şudur:

  • override_settings pytest ile çalışmaz.
  • Django'nun alt TestCasesınıfını oluşturmak işe yarayacaktır ancak o zaman pytest armatürlerini kullanamazsınız.

Çözüm, buradasettings belgelenen fikstürü kullanmaktır .

Misal

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

Ve birden fazla alanı güncellemeniz gerektiğinde

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

Tek bir test işlevi için bile ayarı geçersiz kılabilirsiniz.

from django.test import TestCase, override_settings

class SomeTestCase(TestCase):

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

veya sınıftaki her işlev için ayarı geçersiz kılabilirsiniz.

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

    def test_some_function():
        

1

Pytest kullanıyorum.

Bunu şu şekilde çözmeyi başardım:

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

Testteki ayarları şu şekilde geçersiz kılabilirsiniz:

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"""

Ve aynı ayarlara başka bir dosyada ihtiyacınız varsa, doğrudan içe aktarabilirsiniz test_settings.


0

Bir alt dizine (python paketi) yerleştirilmiş birden fazla test dosyanız varsa, sys.argv'deki 'test' dizesinin varlığı durumuna bağlı olarak tüm bu dosyalar için ayarları geçersiz kılabilirsiniz.

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)

En iyi yaklaşım değil. Celery broker'ı Redis'ten Memory'ye çevirmek için kullandı.


0

Settings.py dosyasından her şeyi içe aktaracak ve test amacıyla farklı olanı değiştirecek yeni bir settings_test.py dosyası oluşturdum. Benim durumumda, test ederken farklı bir bulut depolama paketi kullanmak istedim. görüntü açıklamasını buraya girin

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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.