host-specific request breaks unrelated test cases

179 Views Asked by At

I have the following tests:

from django.test import TestCase
from django.core.urlresolvers import reverse

class TestA(TestCase):
    def test_a(self):
        reverse('view1')

class TestB(TestCase):
    def test_b(self):
        self.client.get('/view2/', HTTP_HOST='second.test.net')

class TestC(TestCase):
    def test_c(self):
        reverse('view1')

TestA and TestB run successfully, but TestC breaks with

..E
======================================================================
ERROR: test_c (dhtest.tests.test_view2.TestC)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/phihag/dhtest/dhtest/tests/test_view2.py", line 14, in test_c
    reverse('view1')
  File "/home/phihag/.local/share/virtualenvs/dhtest---IwXRQ3/lib/python3.6/site-packages/django/urls/base.py", line 91, in reverse
    return force_text(iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs)))
  File "/home/phihag/.local/share/virtualenvs/dhtest---IwXRQ3/lib/python3.6/site-packages/django/urls/resolvers.py", line 497, in _reverse_with_prefix
    raise NoReverseMatch(msg)
django.urls.exceptions.NoReverseMatch: Reverse for 'view1' not found. 'view1' is not a valid view function or pattern name.

----------------------------------------------------------------------
Ran 3 tests in 0.006s

FAILED (errors=1)

But when I comment out TestB, TestC works! How do I fix this problem?

I'm using django-hosts with the following configuration:

from django.conf import settings
from django_hosts import patterns, host

host_patterns = patterns(
    '',
    host(
        r'second\.test\.net',
        'dhtest.secondurls',
        name='second'
    ),
    host(
        r'(\w+)',
        'dhtest.urls',
        name='default'
    ),
)

and fairly simple URL files:

# dhtest/urls.py
from django.conf.urls import url
from django.http import HttpResponse

urlpatterns = [
    url(r'^view1/', lambda _: HttpResponse('This is view1'), name='view1'),
]
# dhtest/secondurls.py
from django.conf.urls import url
from django.http import HttpResponse

urlpatterns = [
    url(r'^view2/', lambda _: HttpResponse('view2 on another host')),
]

For reference, here is the full project.

1

There are 1 best solutions below

0
On

This is a bug in Django-hosts. In its response middleware, django-hosts clobbers the thread-wide urlconf to that of the request.

In production, that is not a problem, because Django resets the urlconf to the default for every request, or the request-specific one.

But during the tests, after TestB there are no more requests coming, and reverse (and a bunch of other URL-related functions) are using the urlconf for a different host.

To work around this, restore the urlconf after any requests to specific hosts, like this:

class TestB(TestCase):
    def test_b(self):
        self.client.get('/view2/', HTTP_HOST='second.test.net')

    def tearDown(self):
        from django.urls.base import set_urlconf
        set_urlconf(None)