I am working on a project that already has hundreds of unit tests, many of which use either the built in django Client, or the django rest framework APIClient for making requests and testing responses.
Having recently implemented the necessaries to make SSL work locally, and setting the SECURE_SSL_REDIRECT
to True (trying to make our dockerised dev
and test
environments as close to production
as possible), I have come a cropper to find that so many unit tests fail, due to the (API)Clients requesting, by default, always using http, not https.
Many (most) requests look like this:
response = self.client.get(some_url)
I am aware that I could use:
response = self.client.get(some_url, secure=True)
But this does mean changing a lot of unit tests. The same is true for using follow=True
, but had the added disadvantage that this could produce some other undesired behaviour.
I cannot see a way of setting the use of secure requests as a default behaviour in the Django Client. I could make my own SecureClient (and SecureAPIClient), but I would then have to make sure that I make a new base TestCase (possibly multiple) to inherit from, and change this everywhere for all the tests - still a lot of work.
It is possible of course to monkey patch the Client, but I am reluctant to to do this as, again, it could have undesired effects that are hard to debug later.
TLDR; Is there a simple (ideally supported) way, to make all unit test requests via the django test's Client, to use SSL by default?
The request factory The RequestFactory shares the same API as the test client. However, instead of behaving like a browser, the RequestFactory provides a way to generate a request instance that can be used as the first argument to any view.
The preferred way to write tests in Django is using the unittest module built-in to the Python standard library. This is covered in detail in the Writing and running tests document. You can also use any other Python test framework; Django provides an API and tools for that kind of integration.
The test client is a Python class that acts as a dummy web browser, allowing you to test your views and interact with your Django-powered application programmatically.
Option 1. Disable the SECURE_SSL_REDIRECT
when needed:
from django.test import override_settings
class FooTest(TestCase):
def setUp(self):
settings_manager = override_settings(SECURE_SSL_REDIRECT=False)
settings_manager.enable()
self.addCleanup(settings_manager.disable)
Option 2: Wrap get
and post
methods of the APIClient
:
class ApiTestCase(TestCase):
def setUp(self):
self.client = APIClient()
def get(self, *args, **kwargs):
return self.client.get(secure=True, *args, **kwargs)
def post(self, *args, **kwargs):
return self.client.post(secure=True, *args, **kwargs)
Option 3: just maintain a separate settings file for the test environment with the SECURE_SSL_REDIRECT
not being set.
I've tried all three options and recommend going with the (3). Here is why:
django.test.client
is just an imitation of a real client. It's there to unit test your views. It doesn't produce real WSGI requests, so test environment won't match production environment anyway.
SECURE_SSL_REDIRECT
is a SecurityMiddleware
setting. Don't unit test third-party code.
Making sure that your code always uses SSL is a good goal. However, this is an integration testing problem. The right tool for this job is Selenium + LiveServerTestCase.
If you're using pytest
and pytest-django
, you can also make a fixture that modifies the test client:
import functools
import pytest
@pytest.fixture
def client(client):
client.get = functools.partial(client.get, secure=True)
client.post = functools.partial(client.post, secure=True)
return client
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With