Fixed #7521 -- Added the ability to customize ROOT_URLCONF for the duration of a TestCase. Thanks to Mark Fargas (telenieko) for his work on this patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@7805 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Russell Keith-Magee 2008-06-30 12:34:29 +00:00
parent 61898d8627
commit 415bd694f9
5 changed files with 81 additions and 5 deletions

View File

@ -21,18 +21,14 @@ class TestForm(forms.Form):
class PreviewTests(TestCase): class PreviewTests(TestCase):
urls = 'django.contrib.formtools.test_urls'
def setUp(self): def setUp(self):
self._old_root_urlconf = settings.ROOT_URLCONF
settings.ROOT_URLCONF = 'django.contrib.formtools.test_urls'
# Create a FormPreview instance to share between tests # Create a FormPreview instance to share between tests
self.preview = preview.FormPreview(TestForm) self.preview = preview.FormPreview(TestForm)
input_template = '<input type="hidden" name="%s" value="%s" />' input_template = '<input type="hidden" name="%s" value="%s" />'
self.input = input_template % (self.preview.unused_name('stage'), "%d") self.input = input_template % (self.preview.unused_name('stage'), "%d")
def tearDown(self):
settings.ROOT_URLCONF = self._old_root_urlconf
def test_unused_name(self): def test_unused_name(self):
""" """
Verifies name mangling to get uniue field name. Verifies name mangling to get uniue field name.

View File

@ -296,3 +296,8 @@ def reverse(viewname, urlconf=None, args=None, kwargs=None):
kwargs = kwargs or {} kwargs = kwargs or {}
return iri_to_uri(u'/' + get_resolver(urlconf).reverse(viewname, *args, **kwargs)) return iri_to_uri(u'/' + get_resolver(urlconf).reverse(viewname, *args, **kwargs))
def clear_url_caches():
global _resolver_cache
global _callable_cache
_resolver_cache.clear()
_callable_cache.clear()

View File

@ -4,10 +4,12 @@ from urlparse import urlsplit, urlunsplit
from django.http import QueryDict from django.http import QueryDict
from django.db import transaction from django.db import transaction
from django.conf import settings
from django.core import mail from django.core import mail
from django.core.management import call_command from django.core.management import call_command
from django.test import _doctest as doctest from django.test import _doctest as doctest
from django.test.client import Client from django.test.client import Client
from django.core.urlresolvers import clear_url_caches
normalize_long_ints = lambda s: re.sub(r'(?<![\w])(\d+)L(?![\w])', '\\1', s) normalize_long_ints = lambda s: re.sub(r'(?<![\w])(\d+)L(?![\w])', '\\1', s)
@ -54,6 +56,8 @@ class TestCase(unittest.TestCase):
* Flushing the database. * Flushing the database.
* If the Test Case class has a 'fixtures' member, installing the * If the Test Case class has a 'fixtures' member, installing the
named fixtures. named fixtures.
* If the Test Case class has a 'urls' member, replace the
ROOT_URLCONF with it.
* Clearing the mail test outbox. * Clearing the mail test outbox.
""" """
call_command('flush', verbosity=0, interactive=False) call_command('flush', verbosity=0, interactive=False)
@ -61,6 +65,10 @@ class TestCase(unittest.TestCase):
# We have to use this slightly awkward syntax due to the fact # We have to use this slightly awkward syntax due to the fact
# that we're using *args and **kwargs together. # that we're using *args and **kwargs together.
call_command('loaddata', *self.fixtures, **{'verbosity': 0}) call_command('loaddata', *self.fixtures, **{'verbosity': 0})
if hasattr(self, 'urls'):
self._old_root_urlconf = settings.ROOT_URLCONF
settings.ROOT_URLCONF = self.urls
clear_url_caches()
mail.outbox = [] mail.outbox = []
def __call__(self, result=None): def __call__(self, result=None):
@ -79,6 +87,23 @@ class TestCase(unittest.TestCase):
result.addError(self, sys.exc_info()) result.addError(self, sys.exc_info())
return return
super(TestCase, self).__call__(result) super(TestCase, self).__call__(result)
try:
self._post_teardown()
except (KeyboardInterrupt, SystemExit):
raise
except Exception:
import sys
result.addError(self, sys.exc_info())
return
def _post_teardown(self):
""" Performs any post-test things. This includes:
* Putting back the original ROOT_URLCONF if it was changed.
"""
if hasattr(self, '_old_root_urlconf'):
settings.ROOT_URLCONF = self._old_root_urlconf
clear_url_caches()
def assertRedirects(self, response, expected_url, status_code=302, def assertRedirects(self, response, expected_url, status_code=302,
target_status_code=200, host=None): target_status_code=200, host=None):

View File

@ -797,6 +797,37 @@ another test, or by the order of test execution.
.. _dumpdata documentation: ../django-admin/#dumpdata-appname-appname .. _dumpdata documentation: ../django-admin/#dumpdata-appname-appname
.. _loaddata documentation: ../django-admin/#loaddata-fixture-fixture .. _loaddata documentation: ../django-admin/#loaddata-fixture-fixture
URLconf configuration
~~~~~~~~~~~~~~~~~~~~~
**New in Django development version**
If your application provides views, you may want to include tests that
use the test client to exercise those views. However, an end user is free
to deploy the views in your application at any URL of their choosing.
This means that your tests can't rely upon the fact that your views will
be available at a particular URL.
In order to provide a reliable URL space for your test,
``django.test.TestCase`` provides the ability to customize the URLconf
configuration for the duration of the execution of a test suite.
If your ``TestCase`` instance defines an ``urls`` attribute, the
``TestCase`` will use the value of that attribute as the ``ROOT_URLCONF``
for the duration of that test.
For example::
from django.test import TestCase
class TestMyViews(TestCase):
urls = 'myapp.test_urls'
def testIndexPageView(self):
# Here you'd test your view using ``Client``.
This test case will use the contents of ``myapp.test_urls`` as the
URLconf for the duration of the test case.
Emptying the test outbox Emptying the test outbox
~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -318,3 +318,22 @@ class ExceptionTests(TestCase):
self.client.get("/test_client_regress/staff_only/") self.client.get("/test_client_regress/staff_only/")
except SuspiciousOperation: except SuspiciousOperation:
self.fail("Staff should be able to visit this page") self.fail("Staff should be able to visit this page")
# We need two different tests to check URLconf subsitution - one to check
# it was changed, and another one (without self.urls) to check it was reverted on
# teardown. This pair of tests relies upon the alphabetical ordering of test execution.
class UrlconfSubstitutionTests(TestCase):
urls = 'regressiontests.test_client_regress.urls'
def test_urlconf_was_changed(self):
"TestCase can enforce a custom URLConf on a per-test basis"
url = reverse('arg_view', args=['somename'])
self.assertEquals(url, '/arg_view/somename/')
# This test needs to run *after* UrlconfSubstitutionTests; the zz prefix in the
# name is to ensure alphabetical ordering.
class zzUrlconfSubstitutionTests(TestCase):
def test_urlconf_was_reverted(self):
"URLconf is reverted to original value after modification in a TestCase"
url = reverse('arg_view', args=['somename'])
self.assertEquals(url, '/test_client_regress/arg_view/somename/')