Search code examples
djangodjango-urlsdjango-testingdjango-settings

During tests, how to override a Django setting used in the urlconf?


In my Django project's urlconf I have something like this:

from django.conf import settings
from django.conf.urls import include, url

urlpatterns = [
    url(r'^{}/blog'.format(settings.MY_ROOT_DIR),
        include('weblogs.urls')),
    # ...
]

In my settings.py I'll have something like:

MY_ROOT_DIR = 'phil'

This is so I can set the root directory, that's used in several places, in one place (the settings).

When I come to test URLs, I use the @override_settings decorator:

from django.test import TestCase, override_settings
from django.urls import reverse

@override_settings(MY_ROOT_DIR='terry')
class WeblogUrlsTestCase(TestCase):

        def test_post_detail_url(self):
            self.assertEqual(
                reverse('post_detail', kwargs={'slug': 'my-post'}),
                '/terry/blog/my-post/'
            )

However, @override_settings doesn't appear to do anything - the MY_ROOT_DIR value used in urls.py is always "phil", never the overridden setting of "terry". I've also tried @modify_settings, and using it on the test method, rather than the class.

I guess the urlconf is is loaded by Django before @override_settings takes effect? Is there a way around this? Or a better solution altogether?


Solution

  • As you guessed, you have overriden settings after they have already been imported in urls. This would have worked if you had used the settings variable inside some method, and not in global scope of urls.py.

    Although overriding settings configuration on runtime might seem convenient, in my opinion, you should create a separate file for testing. This saves a lot of configuration for testing and this would ensure that you never end up doing something irreversible (like cleaning staging database).

    # test_settings.py
    
    MY_ROOT_DIR = 'terry'
    

    Say your testing file exists in 'my_project/test_settings.py', add

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

    in your manage.py. This will ensure that when you run python manage.py test you use test_settings only. If you are using some other testing client like pytest, you could as easily add this to pytest.ini