Search code examples
pythondjangopython-unittestdjango-unittest

Unable to use override_settings class decorator with setUpClass and tearDownClass Django


SOLVED

I am trying to write unittests in django. I encountered different behaviours of override_settings decorator when used with setUpClass and tearDownClass classmethods. Following is the code which doesn't work:

import logging
from django.test import SimpleTestCase, override_settings
from django.http import Http404
from django.core.exceptions import SuspiciousOperation, PermissionDenied
from django.urls import path
from django.views.defaults import server_error

def http_400_view(request):
    """Test view for 400
    """
    raise SuspiciousOperation

def http_403_view(request):
    """Test view for 403
    """
    raise PermissionDenied

def http_404_view(request):
    """Test view for 404
    """
    raise Http404

def http_500_view(request):
    """Test view for 500
    """
    return server_error(request)

urlpatterns = [
    path('400/', http_400_view),
    path('403/', http_403_view),
    path('404/', http_404_view),
    path('500/', http_500_view),
]

@override_settings(ROOT_URLCONF=__name__)
class ErrorCodeHandlerTests(SimpleTestCase):
    """ Tests for error code handlers
    """
    @classmethod
    def setUpClass(cls):
        logging.disable(logging.CRITICAL)

    @classmethod
    def tearDownClass(cls):
        logging.disable(logging.NOTSET)

    status_codes = [400, 403, 404, 500]
    templates = ['400.html', '403.html', '404.html', '500.html']
    user_messages = ['Bad request', 'Permission denied', 'Page not found', 'Internal server error']

    def test_correct_html_rendered_on_error_code(self):
        """Test if correct template and error code exists in response after http errors
        """
        for i in range(len(self.status_codes)):
            with self.subTest(i=i):
                response = self.client.get('/' + str(self.status_codes[i]) + '/')
                self.assertTemplateUsed(response, self.templates[i])
                self.assertContains(
                    response,
                    self.user_messages[i],
                    status_code=self.status_codes[i],
                    html=True
                )

In above code, settings are not overriden and I get 404 for all the urls.

Following is the code which works:

import logging
from django.test import SimpleTestCase, override_settings
from django.http import Http404
from django.core.exceptions import SuspiciousOperation, PermissionDenied
from django.urls import path
from django.views.defaults import server_error

def http_400_view(request):
    """Test view for 400
    """
    raise SuspiciousOperation

def http_403_view(request):
    """Test view for 403
    """
    raise PermissionDenied

def http_404_view(request):
    """Test view for 404
    """
    raise Http404

def http_500_view(request):
    """Test view for 500
    """
    return server_error(request)

urlpatterns = [
    path('400/', http_400_view),
    path('403/', http_403_view),
    path('404/', http_404_view),
    path('500/', http_500_view),
]

class ErrorCodeHandlerTests(SimpleTestCase):
    """ Tests for error code handlers
    """
    @classmethod
    def setUpClass(cls):
        logging.disable(logging.CRITICAL)

    @classmethod
    def tearDownClass(cls):
        logging.disable(logging.NOTSET)

    status_codes = [400, 403, 404, 500]
    templates = ['400.html', '403.html', '404.html', '500.html']
    user_messages = ['Bad request', 'Permission denied', 'Page not found', 'Internal server error']

    @override_settings(ROOT_URLCONF=__name__)
    def test_correct_html_rendered_on_error_code(self):
        """Test if correct template and error code exists in response after http errors
        """
        for i in range(len(self.status_codes)):
            with self.subTest(i=i):
                response = self.client.get('/' + str(self.status_codes[i]) + '/')
                self.assertTemplateUsed(response, self.templates[i])
                self.assertContains(
                    response,
                    self.user_messages[i],
                    status_code=self.status_codes[i],
                    html=True
                )

Also if I don't use setUpClass and tearDownClass, override_settings work in both cases. What can be the possible cause here?


Solution

  • I figured out the issue. Issue is that in setUpClass and tearDownClass I have to call corresponding base class methods as super().setUpClass() and super().tearDownClass() for override_settings to work.