Repository: adamchainz/django-cors-headers

Issue:
Dynamically respond to settings changes
We currently don't use Django's `override_settings` because the settings module determines the values once at import time. It should instead be a dynamic wrapper around `django.conf.settings` that returns the default if it's not set in Django settings, for example see nexus at: https://github.com/YPlan/nexus/blob/master/nexus/conf.py



Discussion:

Resolving patch:
diff --git a/corsheaders/conf.py b/corsheaders/conf.py
new file mode 100644
--- /dev/null
+++ b/corsheaders/conf.py
@@ -0,0 +1,55 @@
+from django.conf import settings
+
+from .defaults import default_headers, default_methods  # Kept here for backwards compatibility
+
+
+class Settings(object):
+    """
+    Shadow Django's settings with a little logic
+    """
+
+    @property
+    def CORS_ALLOW_HEADERS(self):
+        return getattr(settings, 'CORS_ALLOW_HEADERS', default_headers)
+
+    @property
+    def CORS_ALLOW_METHODS(self):
+        return getattr(settings, 'CORS_ALLOW_METHODS', default_methods)
+
+    @property
+    def CORS_ALLOW_CREDENTIALS(self):
+        return getattr(settings, 'CORS_ALLOW_CREDENTIALS', False)
+
+    @property
+    def CORS_PREFLIGHT_MAX_AGE(self):
+        return getattr(settings, 'CORS_PREFLIGHT_MAX_AGE', 86400)
+
+    @property
+    def CORS_ORIGIN_ALLOW_ALL(self):
+        return getattr(settings, 'CORS_ORIGIN_ALLOW_ALL', False)
+
+    @property
+    def CORS_ORIGIN_WHITELIST(self):
+        return getattr(settings, 'CORS_ORIGIN_WHITELIST', ())
+
+    @property
+    def CORS_ORIGIN_REGEX_WHITELIST(self):
+        return getattr(settings, 'CORS_ORIGIN_REGEX_WHITELIST', ())
+
+    @property
+    def CORS_EXPOSE_HEADERS(self):
+        return getattr(settings, 'CORS_EXPOSE_HEADERS', ())
+
+    @property
+    def CORS_URLS_REGEX(self):
+        return getattr(settings, 'CORS_URLS_REGEX', r'^.*$')
+
+    @property
+    def CORS_MODEL(self):
+        return getattr(settings, 'CORS_MODEL', None)
+
+    @property
+    def CORS_REPLACE_HTTPS_REFERER(self):
+        return getattr(settings, 'CORS_REPLACE_HTTPS_REFERER', False)
+
+corsheaders_settings = Settings()
diff --git a/corsheaders/defaults.py b/corsheaders/defaults.py
--- a/corsheaders/defaults.py
+++ b/corsheaders/defaults.py
@@ -1,5 +1,3 @@
-from django.conf import settings
-
 default_headers = (
     'x-requested-with',
     'content-type',
@@ -11,8 +9,6 @@
     'accept-encoding',
 )
 
-CORS_ALLOW_HEADERS = getattr(settings, 'CORS_ALLOW_HEADERS', default_headers)
-
 default_methods = (
     'GET',
     'POST',
@@ -21,28 +17,3 @@
     'DELETE',
     'OPTIONS',
 )
-CORS_ALLOW_METHODS = getattr(settings, 'CORS_ALLOW_METHODS', default_methods)
-
-CORS_ALLOW_CREDENTIALS = getattr(settings, 'CORS_ALLOW_CREDENTIALS', False)
-
-CORS_PREFLIGHT_MAX_AGE = getattr(settings, 'CORS_PREFLIGHT_MAX_AGE', 86400)
-
-CORS_ORIGIN_ALLOW_ALL = getattr(settings, 'CORS_ORIGIN_ALLOW_ALL', False)
-
-CORS_ORIGIN_WHITELIST = getattr(settings, 'CORS_ORIGIN_WHITELIST', ())
-
-CORS_ORIGIN_REGEX_WHITELIST = getattr(
-    settings,
-    'CORS_ORIGIN_REGEX_WHITELIST',
-    ())
-
-CORS_EXPOSE_HEADERS = getattr(settings, 'CORS_EXPOSE_HEADERS', ())
-
-CORS_URLS_REGEX = getattr(settings, 'CORS_URLS_REGEX', '^.*$')
-
-CORS_MODEL = getattr(settings, 'CORS_MODEL', None)
-
-CORS_REPLACE_HTTPS_REFERER = getattr(
-    settings,
-    'CORS_REPLACE_HTTPS_REFERER',
-    False)
diff --git a/corsheaders/middleware.py b/corsheaders/middleware.py
--- a/corsheaders/middleware.py
+++ b/corsheaders/middleware.py
@@ -20,7 +20,7 @@
     # https://docs.djangoproject.com/en/1.10/topics/http/middleware/#upgrading-pre-django-1-10-style-middleware
     MiddlewareMixin = object
 
-from corsheaders import defaults as settings
+from .conf import corsheaders_settings
 
 
 ACCESS_CONTROL_ALLOW_ORIGIN = 'Access-Control-Allow-Origin'
@@ -38,8 +38,7 @@ def _https_referer_replace_reverse(self, request):
         Put the HTTP_REFERER back to its original value and delete the
         temporary storage
         """
-        if (settings.CORS_REPLACE_HTTPS_REFERER and
-                'ORIGINAL_HTTP_REFERER' in request.META):
+        if corsheaders_settings.CORS_REPLACE_HTTPS_REFERER and 'ORIGINAL_HTTP_REFERER' in request.META:
             http_referer = request.META['ORIGINAL_HTTP_REFERER']
             request.META['HTTP_REFERER'] = http_referer
             del request.META['ORIGINAL_HTTP_REFERER']
@@ -67,8 +66,10 @@ def _https_referer_replace(self, request):
         if (request.is_secure() and origin and
                 'ORIGINAL_HTTP_REFERER' not in request.META):
             url = urlparse(origin)
-            if (not settings.CORS_ORIGIN_ALLOW_ALL and
-                    self.origin_not_found_in_white_lists(origin, url)):
+            if (
+                not corsheaders_settings.CORS_ORIGIN_ALLOW_ALL and
+                self.origin_not_found_in_white_lists(origin, url)
+            ):
                 return
 
             try:
@@ -89,7 +90,7 @@ def process_request(self, request):
         view/exception middleware along with the requested view;
         it will call any response middlewares
         """
-        if self.is_enabled(request) and settings.CORS_REPLACE_HTTPS_REFERER:
+        if self.is_enabled(request) and corsheaders_settings.CORS_REPLACE_HTTPS_REFERER:
             self._https_referer_replace(request)
 
         if (self.is_enabled(request) and
@@ -103,7 +104,7 @@ def process_view(self, request, callback, callback_args, callback_kwargs):
         """
         Do the referer replacement here as well
         """
-        if self.is_enabled(request) and settings.CORS_REPLACE_HTTPS_REFERER:
+        if self.is_enabled(request) and corsheaders_settings.CORS_REPLACE_HTTPS_REFERER:
             self._https_referer_replace(request)
         return None
 
@@ -116,45 +117,46 @@ def process_response(self, request, response):
             # todo: check hostname from db instead
             url = urlparse(origin)
 
-            if settings.CORS_MODEL is not None:
-                model = apps.get_model(*settings.CORS_MODEL.split('.'))
+            if corsheaders_settings.CORS_MODEL is not None:
+                model = apps.get_model(*corsheaders_settings.CORS_MODEL.split('.'))
                 if model.objects.filter(cors=url.netloc).exists():
                     response[ACCESS_CONTROL_ALLOW_ORIGIN] = origin
 
-            if (not settings.CORS_ORIGIN_ALLOW_ALL and
-                    self.origin_not_found_in_white_lists(origin, url)):
+            if (
+                not corsheaders_settings.CORS_ORIGIN_ALLOW_ALL and
+                self.origin_not_found_in_white_lists(origin, url)
+            ):
                 return response
 
-            response[ACCESS_CONTROL_ALLOW_ORIGIN] = "*" if (
-                settings.CORS_ORIGIN_ALLOW_ALL and
-                not settings.CORS_ALLOW_CREDENTIALS) else origin
+            if corsheaders_settings.CORS_ORIGIN_ALLOW_ALL and not corsheaders_settings.CORS_ALLOW_CREDENTIALS:
+                response[ACCESS_CONTROL_ALLOW_ORIGIN] = "*"
+            else:
+                response[ACCESS_CONTROL_ALLOW_ORIGIN] = origin
 
-            if len(settings.CORS_EXPOSE_HEADERS):
-                response[ACCESS_CONTROL_EXPOSE_HEADERS] = ', '.join(
-                    settings.CORS_EXPOSE_HEADERS)
+            if len(corsheaders_settings.CORS_EXPOSE_HEADERS):
+                response[ACCESS_CONTROL_EXPOSE_HEADERS] = ', '.join(corsheaders_settings.CORS_EXPOSE_HEADERS)
 
-            if settings.CORS_ALLOW_CREDENTIALS:
+            if corsheaders_settings.CORS_ALLOW_CREDENTIALS:
                 response[ACCESS_CONTROL_ALLOW_CREDENTIALS] = 'true'
 
             if request.method == 'OPTIONS':
-                response[ACCESS_CONTROL_ALLOW_HEADERS] = ', '.join(
-                    settings.CORS_ALLOW_HEADERS)
-                response[ACCESS_CONTROL_ALLOW_METHODS] = ', '.join(
-                    settings.CORS_ALLOW_METHODS)
-                if settings.CORS_PREFLIGHT_MAX_AGE:
-                    response[ACCESS_CONTROL_MAX_AGE] = \
-                        settings.CORS_PREFLIGHT_MAX_AGE
+                response[ACCESS_CONTROL_ALLOW_HEADERS] = ', '.join(corsheaders_settings.CORS_ALLOW_HEADERS)
+                response[ACCESS_CONTROL_ALLOW_METHODS] = ', '.join(corsheaders_settings.CORS_ALLOW_METHODS)
+                if corsheaders_settings.CORS_PREFLIGHT_MAX_AGE:
+                    response[ACCESS_CONTROL_MAX_AGE] = corsheaders_settings.CORS_PREFLIGHT_MAX_AGE
 
         return response
 
     def origin_not_found_in_white_lists(self, origin, url):
-        return (url.netloc not in settings.CORS_ORIGIN_WHITELIST and
-                not self.regex_domain_match(origin))
+        return (
+            url.netloc not in corsheaders_settings.CORS_ORIGIN_WHITELIST and
+            not self.regex_domain_match(origin)
+        )
 
     def regex_domain_match(self, origin):
-        for domain_pattern in settings.CORS_ORIGIN_REGEX_WHITELIST:
+        for domain_pattern in corsheaders_settings.CORS_ORIGIN_REGEX_WHITELIST:
             if re.match(domain_pattern, origin):
                 return origin
 
     def is_enabled(self, request):
-        return re.match(settings.CORS_URLS_REGEX, request.path)
+        return re.match(corsheaders_settings.CORS_URLS_REGEX, request.path)


Test patch:
diff --git a/corsheaders/tests/__init__.py b/corsheaders/tests/__init__.py
new file mode 100644
diff --git a/corsheaders/tests/test_conf.py b/corsheaders/tests/test_conf.py
new file mode 100644
--- /dev/null
+++ b/corsheaders/tests/test_conf.py
@@ -0,0 +1,11 @@
+from django.test.utils import override_settings
+from django.test import SimpleTestCase
+
+from corsheaders.conf import corsheaders_settings
+
+
+class ConfTests(SimpleTestCase):
+
+    @override_settings(CORS_ALLOW_HEADERS=['foo'])
+    def test_can_override(self):
+        assert corsheaders_settings.CORS_ALLOW_HEADERS == ['foo']
diff --git a/corsheaders/tests.py b/corsheaders/tests/test_middleware.py
similarity index 59%
rename from corsheaders/tests.py
rename to corsheaders/tests/test_middleware.py
--- a/corsheaders/tests.py
+++ b/corsheaders/tests/test_middleware.py
@@ -1,6 +1,7 @@
-from django.conf.urls import url
 from django.http import HttpResponse
 from django.test import TestCase
+from django.test.utils import override_settings
+
 from corsheaders.middleware import CorsMiddleware, CorsPostCsrfMiddleware
 from corsheaders.middleware import ACCESS_CONTROL_ALLOW_ORIGIN
 from corsheaders.middleware import ACCESS_CONTROL_EXPOSE_HEADERS
@@ -10,33 +11,6 @@
 from corsheaders.middleware import ACCESS_CONTROL_MAX_AGE
 from corsheaders import defaults as settings
 from mock import Mock
-from mock import patch
-
-
-def test_view(request):
-    return HttpResponse("Test view")
-
-
-def test_view_http401(request):
-    return HttpResponse('Unauthorized', status=401)
-
-
-urlpatterns = [
-    url(r'^test-view/$', test_view, name='test-view'),
-    url(r'^test-view-http401/$', test_view_http401, name='test-view-http401'),
-]
-
-
-class settings_override(object):
-    def __init__(self, **kwargs):
-        self.overrides = kwargs
-
-    def __enter__(self):
-        self.old = dict((key, getattr(settings, key)) for key in self.overrides)
-        settings.__dict__.update(self.overrides)
-
-    def __exit__(self, exc, value, tb):
-        settings.__dict__.update(self.old)
 
 
 class TestCorsMiddlewareProcessRequest(TestCase):
@@ -44,20 +18,20 @@ class TestCorsMiddlewareProcessRequest(TestCase):
     def setUp(self):
         self.middleware = CorsMiddleware()
 
+    @override_settings(CORS_URLS_REGEX='^.*$')
     def test_process_request(self):
         request = Mock(path='/')
         request.method = 'OPTIONS'
         request.META = {'HTTP_ACCESS_CONTROL_REQUEST_METHOD': 'value'}
-        with settings_override(CORS_URLS_REGEX='^.*$'):
-            response = self.middleware.process_request(request)
+        response = self.middleware.process_request(request)
         self.assertIsInstance(response, HttpResponse)
 
+    @override_settings(CORS_URLS_REGEX='^.*$')
     def test_process_request_empty_header(self):
         request = Mock(path='/')
         request.method = 'OPTIONS'
         request.META = {'HTTP_ACCESS_CONTROL_REQUEST_METHOD': ''}
-        with settings_override(CORS_URLS_REGEX='^.*$'):
-            response = self.middleware.process_request(request)
+        response = self.middleware.process_request(request)
         self.assertIsInstance(response, HttpResponse)
 
     def test_process_request_no_header(self):
@@ -74,6 +48,11 @@ def test_process_request_not_options(self):
         response = self.middleware.process_request(request)
         self.assertIsNone(response)
 
+    @override_settings(
+        CORS_URLS_REGEX='^.*$',
+        CORS_ORIGIN_REGEX_WHITELIST='.*google.*',
+        CORS_REPLACE_HTTPS_REFERER=True,
+    )
     def test_process_request_replace_https_referer(self):
         post_middleware = CorsPostCsrfMiddleware()
         request = Mock(path='/')
@@ -85,10 +64,7 @@ def test_process_request_replace_https_referer(self):
             'HTTP_HOST': 'foobar.com',
             'HTTP_ORIGIN': 'https://foo.google.com',
         }
-        with settings_override(CORS_URLS_REGEX='^.*$',
-                               CORS_ORIGIN_REGEX_WHITELIST='.*google.*',
-                               CORS_REPLACE_HTTPS_REFERER=True):
-            response = self.middleware.process_request(request)
+        response = self.middleware.process_request(request)
         self.assertIsNone(response)
 
         # make sure it doesnt blow up when HTTP_HOST is not present
@@ -96,10 +72,7 @@ def test_process_request_replace_https_referer(self):
             'HTTP_REFERER': 'http://foo.google.com/',
             'HTTP_ORIGIN': 'https://foo.google.com',
         }
-        with settings_override(CORS_URLS_REGEX='^.*$',
-                               CORS_ORIGIN_REGEX_WHITELIST='.*google.*',
-                               CORS_REPLACE_HTTPS_REFERER=True):
-            response = self.middleware.process_request(request)
+        response = self.middleware.process_request(request)
         self.assertIsNone(response)
 
         request.is_secure = lambda: False
@@ -110,10 +83,7 @@ def test_process_request_replace_https_referer(self):
         }
 
         # test that we won't replace if the request is not secure
-        with settings_override(CORS_URLS_REGEX='^.*$',
-                               CORS_ORIGIN_REGEX_WHITELIST='.*google.*',
-                               CORS_REPLACE_HTTPS_REFERER=True):
-            response = self.middleware.process_request(request)
+        response = self.middleware.process_request(request)
         self.assertIsNone(response)
         self.assertTrue('ORIGINAL_HTTP_REFERER' not in request.META)
         self.assertEquals(request.META['HTTP_REFERER'], 'http://foo.google.com/')
@@ -126,39 +96,34 @@ def test_process_request_replace_https_referer(self):
         }
 
         # test that we won't replace with the setting off
-        with settings_override(CORS_URLS_REGEX='^.*$',
-                               CORS_ORIGIN_REGEX_WHITELIST='.*google.*'):
+        with override_settings(CORS_REPLACE_HTTPS_REFERER=False):
             response = self.middleware.process_request(request)
         self.assertIsNone(response)
         self.assertTrue('ORIGINAL_HTTP_REFERER' not in request.META)
         self.assertEquals(request.META['HTTP_REFERER'], 'https://foo.google.com/')
 
-        with settings_override(CORS_URLS_REGEX='^.*$',
-                               CORS_ORIGIN_REGEX_WHITELIST='.*google.*',
-                               CORS_REPLACE_HTTPS_REFERER=True):
-            response = self.middleware.process_request(request)
+        response = self.middleware.process_request(request)
         self.assertIsNone(response)
         self.assertEquals(request.META['ORIGINAL_HTTP_REFERER'], 'https://foo.google.com/')
         self.assertEquals(request.META['HTTP_REFERER'], 'https://foobar.com/')
 
         # make sure the replace code is idempotent
-        with settings_override(CORS_URLS_REGEX='^.*$',
-                               CORS_ORIGIN_REGEX_WHITELIST='.*google.*',
-                               CORS_REPLACE_HTTPS_REFERER=True):
-            response = self.middleware.process_view(request, None, None, None)
+        response = self.middleware.process_view(request, None, None, None)
         self.assertIsNone(response)
         self.assertEquals(request.META['ORIGINAL_HTTP_REFERER'], 'https://foo.google.com/')
         self.assertEquals(request.META['HTTP_REFERER'], 'https://foobar.com/')
 
-        with settings_override(CORS_URLS_REGEX='^.*$', CORS_REPLACE_HTTPS_REFERER=True):
-            post_middleware.process_request(request)
+        post_middleware.process_request(request)
         self.assertTrue('ORIGINAL_HTTP_REFERER' not in request.META)
         self.assertEquals(request.META['HTTP_REFERER'], 'https://foo.google.com/')
 
-        with settings_override(CORS_URLS_REGEX='^.*$', CORS_REPLACE_HTTPS_REFERER=True):
-            response = post_middleware.process_request(request)
+        response = post_middleware.process_request(request)
         self.assertIsNone(response)
 
+    @override_settings(
+        CORS_ORIGIN_REGEX_WHITELIST='.*google.*',
+        CORS_REPLACE_HTTPS_REFERER=True,
+    )
     def test_process_view_replace_https_referer(self):
         post_middleware = CorsPostCsrfMiddleware()
         request = Mock(path='/')
@@ -169,25 +134,20 @@ def test_process_view_replace_https_referer(self):
             'HTTP_HOST': 'foobar.com',
             'HTTP_ORIGIN': 'https://foo.google.com',
         }
-        with settings_override(CORS_URLS_REGEX='^.*$',
-                               CORS_ORIGIN_REGEX_WHITELIST='.*google.*',
-                               CORS_REPLACE_HTTPS_REFERER=True):
-            response = self.middleware.process_view(request, None, None, None)
+        response = self.middleware.process_view(request, None, None, None)
         self.assertIsNone(response)
         self.assertEquals(request.META['ORIGINAL_HTTP_REFERER'], 'https://foo.google.com/')
         self.assertEquals(request.META['HTTP_REFERER'], 'https://foobar.com/')
 
-        with settings_override(CORS_URLS_REGEX='^.*$', CORS_REPLACE_HTTPS_REFERER=True):
-            post_middleware.process_view(request, None, None, None)
+        post_middleware.process_view(request, None, None, None)
         self.assertTrue('ORIGINAL_HTTP_REFERER' not in request.META)
         self.assertEquals(request.META['HTTP_REFERER'], 'https://foo.google.com/')
 
-        with settings_override(CORS_URLS_REGEX='^.*$', CORS_REPLACE_HTTPS_REFERER=True):
-            response = post_middleware.process_view(request, None, None, None)
+        response = post_middleware.process_view(request, None, None, None)
         self.assertIsNone(response)
 
 
-@patch('corsheaders.middleware.settings')
+# @patch('corsheaders.middleware.settings')
 class TestCorsMiddlewareProcessResponse(TestCase):
 
     def setUp(self):
@@ -201,39 +161,47 @@ def assertAccessControlAllowOriginEquals(self, response, header):
         )
         self.assertEqual(response[ACCESS_CONTROL_ALLOW_ORIGIN], header)
 
-    def test_process_response_no_origin(self, settings):
-        settings.CORS_MODEL = None
-        settings.CORS_URLS_REGEX = '^.*$'
+    @override_settings(
+        CORS_URLS_REGEX='^.*$',
+        CORS_MODEL=None,
+    )
+    def test_process_response_no_origin(self):
         response = HttpResponse()
         request = Mock(path='/', META={})
         processed = self.middleware.process_response(request, response)
         self.assertNotIn(ACCESS_CONTROL_ALLOW_ORIGIN, processed)
 
-    def test_process_response_not_in_whitelist(self, settings):
-        settings.CORS_MODEL = None
-        settings.CORS_ORIGIN_ALLOW_ALL = False
-        settings.CORS_ORIGIN_WHITELIST = ['example.com']
-        settings.CORS_URLS_REGEX = '^.*$'
+    @override_settings(
+        CORS_ORIGIN_ALLOW_ALL=False,
+        CORS_ORIGIN_WHITELIST=['example.com'],
+        CORS_URLS_REGEX='^.*$',
+        CORS_MODEL=None,
+    )
+    def test_process_response_not_in_whitelist(self):
         response = HttpResponse()
         request = Mock(path='/', META={'HTTP_ORIGIN': 'http://foobar.it'})
         processed = self.middleware.process_response(request, response)
         self.assertNotIn(ACCESS_CONTROL_ALLOW_ORIGIN, processed)
 
-    def test_process_response_in_whitelist(self, settings):
-        settings.CORS_MODEL = None
-        settings.CORS_ORIGIN_ALLOW_ALL = False
-        settings.CORS_ORIGIN_WHITELIST = ['example.com', 'foobar.it']
-        settings.CORS_URLS_REGEX = '^.*$'
+    @override_settings(
+        CORS_ORIGIN_ALLOW_ALL=False,
+        CORS_ORIGIN_WHITELIST=['example.com', 'foobar.it'],
+        CORS_URLS_REGEX='^.*$',
+        CORS_MODEL=None,
+    )
+    def test_process_response_in_whitelist(self):
         response = HttpResponse()
         request = Mock(path='/', META={'HTTP_ORIGIN': 'http://foobar.it'})
         processed = self.middleware.process_response(request, response)
         self.assertAccessControlAllowOriginEquals(processed, 'http://foobar.it')
 
-    def test_process_response_expose_headers(self, settings):
-        settings.CORS_MODEL = None
-        settings.CORS_ORIGIN_ALLOW_ALL = True
-        settings.CORS_EXPOSE_HEADERS = ['accept', 'origin', 'content-type']
-        settings.CORS_URLS_REGEX = '^.*$'
+    @override_settings(
+        CORS_ORIGIN_ALLOW_ALL=True,
+        CORS_EXPOSE_HEADERS=['accept', 'origin', 'content-type'],
+        CORS_URLS_REGEX='^.*$',
+        CORS_MODEL=None,
+    )
+    def test_process_response_expose_headers(self):
         response = HttpResponse()
         request = Mock(path='/', META={'HTTP_ORIGIN': 'http://example.com'})
         processed = self.middleware.process_response(request, response)
@@ -242,59 +210,68 @@ def test_process_response_expose_headers(self, settings):
             'accept, origin, content-type'
         )
 
-    def test_process_response_dont_expose_headers(self, settings):
-        settings.CORS_MODEL = None
-        settings.CORS_ORIGIN_ALLOW_ALL = True
-        settings.CORS_EXPOSE_HEADERS = []
-        settings.CORS_URLS_REGEX = '^.*$'
+    @override_settings(
+        CORS_ORIGIN_ALLOW_ALL=True,
+        CORS_EXPOSE_HEADERS=[],
+        CORS_URLS_REGEX='^.*$',
+        CORS_MODEL=None,
+    )
+    def test_process_response_dont_expose_headers(self):
         response = HttpResponse()
         request = Mock(path='/', META={'HTTP_ORIGIN': 'http://example.com'})
         processed = self.middleware.process_response(request, response)
         self.assertNotIn(ACCESS_CONTROL_EXPOSE_HEADERS, processed)
 
-    def test_process_response_allow_credentials(self, settings):
-        settings.CORS_MODEL = None
-        settings.CORS_ORIGIN_ALLOW_ALL = True
-        settings.CORS_ALLOW_CREDENTIALS = True
-        settings.CORS_URLS_REGEX = '^.*$'
+    @override_settings(
+        CORS_ALLOW_CREDENTIALS=True,
+        CORS_ORIGIN_ALLOW_ALL=True,
+        CORS_URLS_REGEX='^.*$',
+        CORS_MODEL=None,
+    )
+    def test_process_response_allow_credentials(self):
         response = HttpResponse()
         request = Mock(path='/', META={'HTTP_ORIGIN': 'http://example.com'})
         processed = self.middleware.process_response(request, response)
         self.assertEqual(processed[ACCESS_CONTROL_ALLOW_CREDENTIALS], 'true')
 
-    def test_process_response_dont_allow_credentials(self, settings):
-        settings.CORS_MODEL = None
-        settings.CORS_ORIGIN_ALLOW_ALL = True
-        settings.CORS_ALLOW_CREDENTIALS = False
-        settings.CORS_URLS_REGEX = '^.*$'
+    @override_settings(
+        CORS_ALLOW_CREDENTIALS=False,
+        CORS_ORIGIN_ALLOW_ALL=True,
+        CORS_URLS_REGEX='^.*$',
+        CORS_MODEL=None,
+    )
+    def test_process_response_dont_allow_credentials(self):
         response = HttpResponse()
         request = Mock(path='/', META={'HTTP_ORIGIN': 'http://example.com'})
         processed = self.middleware.process_response(request, response)
         self.assertNotIn(ACCESS_CONTROL_ALLOW_CREDENTIALS, processed)
 
-    def test_process_response_options_method(self, settings):
-        settings.CORS_MODEL = None
-        settings.CORS_ORIGIN_ALLOW_ALL = True
-        settings.CORS_ALLOW_HEADERS = ['content-type', 'origin']
-        settings.CORS_ALLOW_METHODS = ['GET', 'OPTIONS']
-        settings.CORS_PREFLIGHT_MAX_AGE = 1002
-        settings.CORS_URLS_REGEX = '^.*$'
+    @override_settings(
+        CORS_ALLOW_HEADERS=['content-type', 'origin'],
+        CORS_ALLOW_METHODS=['GET', 'OPTIONS'],
+        CORS_PREFLIGHT_MAX_AGE=1002,
+        CORS_ORIGIN_ALLOW_ALL=True,
+        CORS_URLS_REGEX='^.*$',
+        CORS_MODEL=None,
+    )
+    def test_process_response_options_method(self):
         response = HttpResponse()
         request_headers = {'HTTP_ORIGIN': 'http://example.com'}
         request = Mock(path='/', META=request_headers, method='OPTIONS')
         processed = self.middleware.process_response(request, response)
-        self.assertEqual(processed[ACCESS_CONTROL_ALLOW_HEADERS],
-            'content-type, origin')
+        self.assertEqual(processed[ACCESS_CONTROL_ALLOW_HEADERS], 'content-type, origin')
         self.assertEqual(processed[ACCESS_CONTROL_ALLOW_METHODS], 'GET, OPTIONS')
         self.assertEqual(processed[ACCESS_CONTROL_MAX_AGE], '1002')
 
-    def test_process_response_options_method_no_max_age(self, settings):
-        settings.CORS_MODEL = None
-        settings.CORS_ORIGIN_ALLOW_ALL = True
-        settings.CORS_ALLOW_HEADERS = ['content-type', 'origin']
-        settings.CORS_ALLOW_METHODS = ['GET', 'OPTIONS']
-        settings.CORS_PREFLIGHT_MAX_AGE = 0
-        settings.CORS_URLS_REGEX = '^.*$'
+    @override_settings(
+        CORS_ALLOW_HEADERS=['content-type', 'origin'],
+        CORS_ALLOW_METHODS=['GET', 'OPTIONS'],
+        CORS_PREFLIGHT_MAX_AGE=0,
+        CORS_ORIGIN_ALLOW_ALL=True,
+        CORS_URLS_REGEX='^.*$',
+        CORS_MODEL=None,
+    )
+    def test_process_response_options_method_no_max_age(self):
         response = HttpResponse()
         request_headers = {'HTTP_ORIGIN': 'http://example.com'}
         request = Mock(path='/', META=request_headers, method='OPTIONS')
@@ -306,67 +283,85 @@ def test_process_response_options_method_no_max_age(self, settings):
         self.assertEqual(processed[ACCESS_CONTROL_ALLOW_METHODS], 'GET, OPTIONS')
         self.assertNotIn(ACCESS_CONTROL_MAX_AGE, processed)
 
-    def test_process_response_whitelist_with_port(self, settings):
-        settings.CORS_MODEL = None
-        settings.CORS_ORIGIN_ALLOW_ALL = False
-        settings.CORS_ALLOW_METHODS = ['OPTIONS']
-        settings.CORS_ORIGIN_WHITELIST = ('localhost:9000',)
-        settings.CORS_URLS_REGEX = '^.*$'
+    @override_settings(
+        CORS_ALLOW_METHODS=['OPTIONS'],
+        CORS_ALLOW_CREDENTIALS=True,
+        CORS_ORIGIN_ALLOW_ALL=False,
+        CORS_ORIGIN_WHITELIST=('localhost:9000',),
+        CORS_URLS_REGEX='^.*$',
+        CORS_MODEL=None,
+    )
+    def test_process_response_whitelist_with_port(self):
         response = HttpResponse()
         request_headers = {'HTTP_ORIGIN': 'http://localhost:9000'}
         request = Mock(path='/', META=request_headers, method='OPTIONS')
         processed = self.middleware.process_response(request, response)
         self.assertEqual(processed.get(ACCESS_CONTROL_ALLOW_CREDENTIALS, None), 'true')
 
-    def test_process_response_adds_origin_when_domain_found_in_origin_regex_whitelist(self, settings):
-        settings.CORS_MODEL = None
-        settings.CORS_ORIGIN_REGEX_WHITELIST = ('^http?://(\w+\.)?google\.com$', )
-        settings.CORS_ALLOW_CREDENTIALS = True
-        settings.CORS_ORIGIN_ALLOW_ALL = False
-        settings.CORS_ALLOW_METHODS = ['OPTIONS']
-        settings.CORS_URLS_REGEX = '^.*$'
+    @override_settings(
+        CORS_ALLOW_METHODS=['OPTIONS'],
+        CORS_ALLOW_CREDENTIALS=True,
+        CORS_ORIGIN_ALLOW_ALL=False,
+        CORS_ORIGIN_REGEX_WHITELIST=('^http?://(\w+\.)?google\.com$',),
+        CORS_URLS_REGEX='^.*$',
+        CORS_MODEL=None,
+    )
+    def test_process_response_adds_origin_when_domain_found_in_origin_regex_whitelist(self):
         response = HttpResponse()
         request_headers = {'HTTP_ORIGIN': 'http://foo.google.com'}
         request = Mock(path='/', META=request_headers, method='OPTIONS')
         processed = self.middleware.process_response(request, response)
         self.assertEqual(processed.get(ACCESS_CONTROL_ALLOW_ORIGIN, None), 'http://foo.google.com')
 
-    def test_process_response_will_not_add_origin_when_domain_not_found_in_origin_regex_whitelist(self, settings):
-        settings.CORS_MODEL = None
-        settings.CORS_ORIGIN_REGEX_WHITELIST = ('^http?://(\w+\.)?yahoo\.com$', )
-        settings.CORS_ALLOW_CREDENTIALS = True
-        settings.CORS_ORIGIN_ALLOW_ALL = False
-        settings.CORS_ALLOW_METHODS = ['OPTIONS']
-        settings.CORS_URLS_REGEX = '^.*$'
+    @override_settings(
+        CORS_ALLOW_METHODS=['OPTIONS'],
+        CORS_ALLOW_CREDENTIALS=True,
+        CORS_ORIGIN_ALLOW_ALL=False,
+        CORS_ORIGIN_REGEX_WHITELIST=('^http?://(\w+\.)?yahoo\.com$',),
+        CORS_URLS_REGEX='^.*$',
+        CORS_MODEL=None,
+    )
+    def test_process_response_will_not_add_origin_when_domain_not_found_in_origin_regex_whitelist(self):
         response = HttpResponse()
         request_headers = {'HTTP_ORIGIN': 'http://foo.google.com'}
         request = Mock(path='/', META=request_headers, method='OPTIONS')
         processed = self.middleware.process_response(request, response)
         self.assertEqual(processed.get(ACCESS_CONTROL_ALLOW_ORIGIN, None), None)
 
-    def test_process_response_when_custom_model_enabled(self, settings):
+    @override_settings(
+        CORS_ALLOW_METHODS=settings.default_methods,
+        CORS_ALLOW_CREDENTIALS=False,
+        CORS_ORIGIN_ALLOW_ALL=False,
+        CORS_ORIGIN_REGEX_WHITELIST=(),
+        CORS_URLS_REGEX='^.*$',
+        CORS_MODEL='corsheaders.CorsModel',
+    )
+    def test_process_response_when_custom_model_enabled(self):
         from corsheaders.models import CorsModel
         CorsModel.objects.create(cors='foo.google.com')
-        settings.CORS_ORIGIN_REGEX_WHITELIST = ()
-        settings.CORS_ALLOW_CREDENTIALS = False
-        settings.CORS_ORIGIN_ALLOW_ALL = False
-        settings.CORS_ALLOW_METHODS = settings.default_methods
-        settings.CORS_URLS_REGEX = '^.*$'
-        settings.CORS_MODEL = 'corsheaders.CorsModel'
         response = HttpResponse()
         request = Mock(path='/', META={'HTTP_ORIGIN': 'http://foo.google.com'})
         processed = self.middleware.process_response(request, response)
         self.assertEqual(processed.get(ACCESS_CONTROL_ALLOW_ORIGIN, None), 'http://foo.google.com')
 
-    def test_middleware_integration_get(self, settings):
-        settings.CORS_MODEL = None
-        settings.CORS_ORIGIN_ALLOW_ALL = True
-        settings.CORS_URLS_REGEX = '^.*$'
+    @override_settings(
+        CORS_ALLOW_CREDENTIALS=True,
+        CORS_ORIGIN_ALLOW_ALL=True,
+        CORS_URLS_REGEX='^.*$',
+        CORS_MODEL=None,
+    )
+    def test_middleware_integration_get(self):
         response = self.client.get('/test-view/', HTTP_ORIGIN='http://foobar.it')
         self.assertEqual(response.status_code, 200)
         self.assertAccessControlAllowOriginEquals(response, 'http://foobar.it')
 
-    def test_middleware_integration_get_auth_view(self, settings):
+    @override_settings(
+        CORS_ALLOW_CREDENTIALS=True,
+        CORS_ORIGIN_ALLOW_ALL=True,
+        CORS_URLS_REGEX='^.*$',
+        CORS_MODEL=None,
+    )
+    def test_middleware_integration_get_auth_view(self):
         """
         It's not clear whether the header should still be set for non-HTTP200
         when not a preflight request. However this is the existing behaviour for
@@ -375,21 +370,21 @@ def test_middleware_integration_get_auth_view(self, settings):
         place to preserve this behaviour. See `ExceptionMiddleware` mention here:
         https://docs.djangoproject.com/en/1.10/topics/http/middleware/#upgrading-pre-django-1-10-style-middleware
         """
-        settings.CORS_MODEL = None
-        settings.CORS_ORIGIN_ALLOW_ALL = True
-        settings.CORS_URLS_REGEX = '^.*$'
         response = self.client.get('/test-view-http401/', HTTP_ORIGIN='http://foobar.it')
         self.assertEqual(response.status_code, 401)
         self.assertAccessControlAllowOriginEquals(response, 'http://foobar.it')
 
-    def test_middleware_integration_preflight_auth_view(self, settings):
+    @override_settings(
+        CORS_ALLOW_CREDENTIALS=True,
+        CORS_ORIGIN_ALLOW_ALL=True,
+        CORS_URLS_REGEX='^.*$',
+        CORS_MODEL=None,
+    )
+    def test_middleware_integration_preflight_auth_view(self):
         """
         Ensure HTTP200 and header still set, for preflight requests to views requiring
         authentication. See: https://github.com/ottoyiu/django-cors-headers/issues/3
         """
-        settings.CORS_MODEL = None
-        settings.CORS_ORIGIN_ALLOW_ALL = True
-        settings.CORS_URLS_REGEX = '^.*$'
         response = self.client.options('/test-view-http401/',
                                        HTTP_ORIGIN='http://foobar.it',
                                        HTTP_ACCESS_CONTROL_REQUEST_METHOD='value')
diff --git a/corsheaders/tests/urls.py b/corsheaders/tests/urls.py
new file mode 100644
--- /dev/null
+++ b/corsheaders/tests/urls.py
@@ -0,0 +1,16 @@
+from django.conf.urls import url
+from django.http import HttpResponse
+
+
+def test_view(request):
+    return HttpResponse("Test view")
+
+
+def test_view_http401(request):
+    return HttpResponse('Unauthorized', status=401)
+
+
+urlpatterns = [
+    url(r'^test-view/$', test_view, name='test-view'),
+    url(r'^test-view-http401/$', test_view_http401, name='test-view-http401'),
+]
diff --git a/tests.py b/tests.py
--- a/tests.py
+++ b/tests.py
@@ -27,7 +27,7 @@ def run_tests():
                 'TEST_NAME': ':memory:',
             },
         },
-        'ROOT_URLCONF': 'corsheaders.tests',
+        'ROOT_URLCONF': 'corsheaders.tests.urls',
         middleware_setting: middleware,
     }
     settings.configure(**config)