django-hosts
django-hosts copied to clipboard
Testing related issues; documentation & helpers needed to django-hosts ?
Some things I stumbled upon that others might find useful
Issue 1: how to make sure the right urlconf is used based on host when running tests
Some helpers to survive tests:
class DjangoHostsTestCase(TestCase):
"""Extends django.test.TestCase by setting a default SERVER_NAME to test client.
This is most likely necessary when testing code using django_hosts.
"""
@property
def client(self):
return self._client
@client.setter
def client(self, value):
value.defaults.setdefault('SERVER_NAME', 'the-new-hostname')
self._client = value
def reverse(*args, **kwargs):
"""Improved reverse that uses a specific django_hosts host."""
kwargs.setdefault('host', 'the-default-host')
return django_hosts.resolvers.reverse(*args, **kwargs)
NOTE: you could also just
response = self.client.get('/', SERVER_NAME='the-new-hostname')
Issue 2: django_hosts messes up with my existing tests that use reverse (namespace not registed)
Add following to TestCase:
from django.urls.base import set_urlconf
def tearDown(self):
# revert urlconf as django_hosts & self.client SERVER_NAME mess with it
set_urlconf(settings.ROOT_URLCONF)
Can't you just do something like:
@override_settings(DEFAULT_HOST='the-new-hostname')
class MyTest(TestCase):
....
it doesn't work with host_url in templates.
I recently joined a project where the tests are using the host
header:
from django.test import TestCase
from django_hosts import reverse
class UserViewTestCase(TestCase):
def test_user_list(self):
# The url looks like http://api.example.com/v1/users/
url = reverse("list-user", host="api")
# Repeat the host in the requests (instead of using SERVER_NAME)
response = self.client.get(url, headers={"host": "api.example.com"})
assert response.status_code == 200
Which I didn't find particularly DRY. I just found out about the SERVER_NAME
, but it feels like it's also repeating ourselves.
The Django TestCase
class actually has a client_class
attribute to control which client class is used, so I created a custom client class, which one could use in a custom test case class:
from urllib.parse import urlparse
from django.test import Client
class HostsClient(Client):
def generic(self, method, path, *args, **extra):
"""Used by all methods."""
if path.startswith("http"):
# Populate the host header from the URL host, to play nicely with django-hosts
_scheme, netloc, *_others = urlparse(path)
extra.setdefault("headers", {})
extra["headers"]["host"] = netloc
return super().generic(method, path, *args, **extra)
Which turns the test into:
class UserViewTestCase(TestCase):
client_class = HostsClient
def test_user_list(self):
url = reverse("list-user", host="api")
# No need to set the host header anymore
response = self.client.get(url)
assert response.status_code == 200
Feels a bit simpler than passing the header/server name for each call to client.get(...)
/ client.post(...)
/ ... Now if you combine this with @coler-j suggestion, the test is even simpler:
@override_settings(DEFAULT_HOST='api')
class UserViewTestCase(TestCase):
client_class = HostsClient
def test_user_list(self):
url = reverse("list-user")
response = self.client.get(url)
assert response.status_code == 200
Which looks almost like vanilla Django. Would you be interested in including this test client to django-hosts and update the documentation accordingly?
I like @browniebroke 's solution. It looks pretty neat. Would be great to have it in the official package.
Perhaps we could include a new TestCase
too?
from django.test import TestCase as DjangoTestCase
class TestCase(DjangoTestCase):
client_class = HostsClient
I've gotten around to add this https://github.com/jazzband/django-hosts/pull/169 - open to feedback