Django Pytest Test URL Based on Settings
Asked Answered
I

3

9

I have an endpoint /docs in django that I only want to be visible when DEBUG = True in settings - otherwise, it should throw a 404. My setup looks like this

urls.py

urlpatterns = ...

if settings.DEBUG:
    urlpatterns += [
            url(r'^docs/$', SwaggerSchemaView.as_view(), name='api_docs'),
    ]

When doing testing, though, django doesn't automatically reload urls.py, which means simply overriding DEBUG to True or False doesn't work.

My tests look something like this

@override_settings(DEBUG=True)
@override_settings(ROOT_URLCONF='config.urls')
class APIDocsTestWithDebug(APITestCase):
    # check for 200s
    ...

@override_settings(DEBUG=False)
@override_settings(ROOT_URLCONF='config.urls')
class APIDocsTestWithoutDebug(APITestCase):
    # check for 404s
    ...

Now here's the weird part: When I run the tests individually using pytest path/to/test.py::APIDocsTestWithDebug and pytest path/to/test.py::APIDocsTestWithoutDebug, both tests pass. However, if I run the test file as a whole (pytest path/to/test.py), APIDocsTestWithDebug always fails. The fact that they work individually but not together tells me that the url override is working, but when the tests are in tandem, there is some bug that messes things up. I was wondering if anybody had come across a similar issue and either has an entirely different solution or can give me some tips as to what I'm doing wrong.

Iamb answered 18/7, 2017 at 17:17 Comment(5)
I wouldn't say so - that question is an issue with the core functionality of overriding settings - my issue is that, while I can get DEBUG to be overridden, my urls are defined based on whether or not DEBUG is True. I need a find a way to re-generate the URLs, or something similarIamb
How are you importing settings in urls.py?Mcallister
from django.conf import settingsIamb
I'll take down the flag. You're right about it being a different issue.Mcallister
You can try using includeMcallister
E
14

I struggled with the same issue. The thing is that Django loads your urlpatterns once while initializing - and overriding the settings with the decorator doesn't change what was initially loaded.

Here's what worked for me - try reloading your urls module (based on this) and clearing url caches with clear_url_caches() before the failing test cases:

import sys

from importlib import reload, import_module

from django.conf import settings
from django.core.urlresolvers import clear_url_caches  # Or -> from django.urls import clear_url_caches

def reload_urlconf(urlconf=None):
    clear_url_caches()
    if urlconf is None:
        urlconf = settings.ROOT_URLCONF
    if urlconf in sys.modules:
        reload(sys.modules[urlconf])
    else:
        import_module(urlconf)

PS: You might also want to restore the urlpatterns later - just run reload_urlconf within other settings.

Edmead answered 4/9, 2017 at 10:7 Comment(4)
Thank you, great answer.Brahmani
this is correct. url registry loads only once both in a test suite and on app run. so the initial urlpatterns (based off the env var) on load won't change even if you change the env var at run time. you can change the env var then reload but make sure to revert it assuming reseting state is the intention.Neldanelia
This did it for me, in newer versions of django, you need to use from django.urls import clear_url_caches instead of from django.core.urlresolversArgentic
great answer I've done a pypi package based on this code, but using a context manager for loading on enter and reloading on exit github: github.com/karpyncho/reload-urls installing: pip install karpyncho_reload_urlsHydrangea
M
4

You can use @pytest.mark.urls: https://pytest-django.readthedocs.io/en/latest/helpers.html#pytest.mark.urls

@pytest.mark.urls('myapp.test_urls')
def test_something(client):
    assert 'Success!' in client.get('/some_url_defined_in_test_urls/').content

You could even define the URLs within the same file:

def some_view(request):
    return HttpResponse(b"Success!")


urlpatterns = [
    path("some-url/", some_view)
]


@pytest.mark.urls(__name__)
def test_something(client):
    assert b'Success!' in client.get('/some-url/').content
Meant answered 30/1, 2020 at 11:11 Comment(0)
H
0

Based on Sergei Nikiforov's Answer I have implemented a Pypi package with a context manager for loading/reloading urls

Github Page: https://github.com/karpyncho/reload-urls

Install:

pip install karpyncho_reload_urls

then you simply inherits your TestCase suit class from TestCaseReloadableURL

and you can simply use:

from karpyncho.reload_urls import TestCaseReloadableURL
 
class TestMyClass(TestCaseReloadableURL)
    def my_test(self):
        with self.reload_urls(DEBUG=True):
            # put your checks here

that will reload your settings only in the context block

you are free to propose improvements...

Hydrangea answered 16/4, 2023 at 22:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.