views:

629

answers:

4

Is there any simple mechanism for overriding Django settings for a unit test? I have a manager on one of my models that returns a specific number of the latest objects. The number of objects it returns is defined by a NUM_LATEST setting.

This has the potential to make my tests fail if someone were to change the setting. How can I override the settings on setUp() and subsequently restore them on tearDown()? If that isn't possible, is there some way I can monkey patch the method or mock the settings?

EDIT: Here is my manager code:

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]

The manager uses settings.NEWS_LATEST_MAX to slice the queryset. The getattr() is simply used to provide a default should the setting not exist.

+1  A: 

Since modules are just namespaces, you can just do:

import settings
settings.name = value
Daniel
+8  A: 

You can do anything you like to the UnitTest subclass, including setting and reading instance properties:

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

Since the django test cases run single-threaded, however, I'm curious about what else may be modifying the NUM_LATEST value? If that "something else" is triggered by your test routine, then I'm not sure any amount of monkey patching will save the test without invalidating the veracity of the tests itself.

Jarret Hardie
I added a code example.
Soviut
Ah, gotcha... code example clears that up.
Jarret Hardie
Your example worked. This has been an eye-opener in terms of the scope of unit testing and how the settings in the tests file propagate down through the call stack.
Soviut
This does not work with `settings.TEMPLATE_LOADERS`... So this is not general way at least, the settings or Django is not reloaded or anything with this trick.
Ciantic
+1  A: 

Found this while trying to fix some doctests... For completeness I want to mention that if you're going to modify the settings when using doctests, you should do it before importing anything else...

>>> from django.conf import settings

>>> settings.SOME_SETTING = 20

>>> # Your other imports
>>> from django.core.paginator import Paginator
>>> # etc
Jiaaro
+2  A: 

If you change settings frequently in your tests and use Python ≥2.5, this is also handy:

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)

Then you can do:

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