2009-05-27 67 views
82

有沒有簡單的機制來覆蓋單元測試的Django設置?我在我的一個模型上有一個管理器,它返回特定數量的最新對象。它返回的對象數量由NUM_LATEST設置定義。如何在Django中使用不同的設置進行單元測試?

如果有人要改變設置,這有可能使我的測試失敗。如何覆蓋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()僅用於提供默認值。

+0

@Anto - 你能解釋一下爲什麼或者提供更好的答案嗎? – user 2015-01-13 14:37:24

+0

在此期間改變了;前者接受了一個[this one](http://stackoverflow.com/a/913596/1030960);) – Anto 2015-01-14 01:28:40

回答

134

編輯:這個答案適用,如果你想更改特定測試的設置。

由於Django的1.4,有辦法在測試過程中,以覆蓋設置: https://docs.djangoproject.com/en/dev/topics/testing/tools/#overriding-settings

的TestCase將有self.settings上下文管理器,並有也將是一個@override_settings裝飾可以應用到任何一個測試方法或整個TestCase子類。

這些功能在Django 1.3中尚不存在。

如果您想要更改全部的測試設置,您需要爲測試創建一個單獨的設置文件,該文件可以加載並覆蓋主設置文件中的設置。在其他答案中有幾個很好的方法來解決這個問題。我已經看到hspander'sdmitrii's方法的成功變體。

41

你可以做任何你喜歡的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值?如果這個「別的東西」是由你的測試例程觸發的,那麼我不確定任何數量的猴子補丁都能保存測試,而不會使測試本身的準確性無效。

+0

我添加了一個代碼示例。 – Soviut 2009-05-27 01:59:32

+0

啊,呃...代碼示例清除了這個。 – 2009-05-27 02:02:39

+0

你的例子工作。在單元測試的範圍以及測試文件中的設置如何通過調用堆棧向下傳播時,這讓人大開眼界。 – Soviut 2009-05-27 03:48:05

3

發現這一點的同時試圖解決一些文檔測試......爲了完整性我想提一提,如果你打算使用文檔測試時修改設置,應在導入任何其他事情之前做...

>>> from django.conf import settings 

>>> settings.SOME_SETTING = 20 

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

更新:以下解決方案僅在Django 1.3.x及更早版本中需要。對於> 1.4,請參閱slinkp's answer

如果您經常更改設置在您的測試和使用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() 
9

儘管在運行時重寫設置配置可能有所幫助,但我認爲您應該創建一個單獨的文件進行測試。這爲測試節省了大量配置,這將確保您永遠不會做不可逆轉的事情(如清理臨時數據庫)。

說你的測試文件 'MY_PROJECT/test_settings.py' 存在,在你的manage.py添加

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

。這將確保您在運行python manage.py test時僅使用test_settings。如果您使用的是一些其他的測試客戶端一樣pytest,你可以像它們運行測試時增加這pytest.ini

7

你可以通過--settings選項

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

@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 
相關問題