2007-05-31 21:18:12 +08:00
|
|
|
import re, unittest
|
2007-08-31 19:37:28 +08:00
|
|
|
from urlparse import urlsplit
|
|
|
|
from django.http import QueryDict
|
2006-08-27 20:24:59 +08:00
|
|
|
from django.db import transaction
|
2007-08-16 14:06:55 +08:00
|
|
|
from django.core import mail
|
|
|
|
from django.core.management import call_command
|
2007-03-01 21:11:08 +08:00
|
|
|
from django.db.models import get_apps
|
2007-05-31 21:18:12 +08:00
|
|
|
from django.test import _doctest as doctest
|
2007-05-05 11:03:33 +08:00
|
|
|
from django.test.client import Client
|
|
|
|
|
2006-08-27 20:24:59 +08:00
|
|
|
normalize_long_ints = lambda s: re.sub(r'(?<![\w])(\d+)L(?![\w])', '\\1', s)
|
|
|
|
|
|
|
|
class OutputChecker(doctest.OutputChecker):
|
|
|
|
def check_output(self, want, got, optionflags):
|
|
|
|
ok = doctest.OutputChecker.check_output(self, want, got, optionflags)
|
|
|
|
|
|
|
|
# Doctest does an exact string comparison of output, which means long
|
|
|
|
# integers aren't equal to normal integers ("22L" vs. "22"). The
|
|
|
|
# following code normalizes long integers so that they equal normal
|
|
|
|
# integers.
|
|
|
|
if not ok:
|
|
|
|
return normalize_long_ints(want) == normalize_long_ints(got)
|
|
|
|
return ok
|
|
|
|
|
|
|
|
class DocTestRunner(doctest.DocTestRunner):
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
doctest.DocTestRunner.__init__(self, *args, **kwargs)
|
|
|
|
self.optionflags = doctest.ELLIPSIS
|
|
|
|
|
|
|
|
def report_unexpected_exception(self, out, test, example, exc_info):
|
|
|
|
doctest.DocTestRunner.report_unexpected_exception(self,out,test,example,exc_info)
|
|
|
|
|
|
|
|
# Rollback, in case of database errors. Otherwise they'd have
|
|
|
|
# side effects on other tests.
|
|
|
|
from django.db import transaction
|
|
|
|
transaction.rollback_unless_managed()
|
|
|
|
|
2007-03-01 21:11:08 +08:00
|
|
|
class TestCase(unittest.TestCase):
|
2007-05-08 19:19:34 +08:00
|
|
|
def _pre_setup(self):
|
|
|
|
"""Perform any pre-test setup. This includes:
|
2007-03-01 21:11:08 +08:00
|
|
|
|
2007-05-08 19:19:34 +08:00
|
|
|
* If the Test Case class has a 'fixtures' member, clearing the
|
|
|
|
database and installing the named fixtures at the start of each test.
|
|
|
|
* Clearing the mail test outbox.
|
|
|
|
|
2007-03-01 21:11:08 +08:00
|
|
|
"""
|
2007-08-16 14:06:55 +08:00
|
|
|
call_command('flush', verbosity=0, interactive=False)
|
2007-03-01 21:11:08 +08:00
|
|
|
if hasattr(self, 'fixtures'):
|
2007-08-16 14:06:55 +08:00
|
|
|
# We have to use this slightly awkward syntax due to the fact
|
|
|
|
# that we're using *args and **kwargs together.
|
|
|
|
call_command('loaddata', *self.fixtures, **{'verbosity': 0})
|
2007-05-08 19:19:34 +08:00
|
|
|
mail.outbox = []
|
2007-05-13 00:53:27 +08:00
|
|
|
|
|
|
|
def __call__(self, result=None):
|
|
|
|
"""
|
|
|
|
Wrapper around default __call__ method to perform common Django test
|
|
|
|
set up. This means that user-defined Test Cases aren't required to
|
|
|
|
include a call to super().setUp().
|
2007-03-01 21:11:08 +08:00
|
|
|
"""
|
2007-05-05 11:03:33 +08:00
|
|
|
self.client = Client()
|
2007-05-08 19:19:34 +08:00
|
|
|
self._pre_setup()
|
2007-05-13 00:53:27 +08:00
|
|
|
super(TestCase, self).__call__(result)
|
2007-05-05 11:03:33 +08:00
|
|
|
|
2007-08-31 19:37:28 +08:00
|
|
|
def assertRedirects(self, response, expected_url, status_code=302, target_status_code=200):
|
2007-05-10 19:27:59 +08:00
|
|
|
"""Assert that a response redirected to a specific URL, and that the
|
2007-05-05 11:03:33 +08:00
|
|
|
redirect URL can be loaded.
|
|
|
|
|
2007-08-31 19:37:28 +08:00
|
|
|
Note that assertRedirects won't work for external links since it uses
|
|
|
|
TestClient to do a request.
|
2007-05-05 11:03:33 +08:00
|
|
|
"""
|
2007-05-10 19:27:59 +08:00
|
|
|
self.assertEqual(response.status_code, status_code,
|
2007-07-05 20:54:42 +08:00
|
|
|
"Response didn't redirect as expected: Response code was %d (expected %d)" %
|
2007-05-10 19:27:59 +08:00
|
|
|
(response.status_code, status_code))
|
2007-08-31 19:37:28 +08:00
|
|
|
scheme, netloc, path, query, fragment = urlsplit(response['Location'])
|
|
|
|
url = path
|
|
|
|
if query:
|
|
|
|
url += '?' + query
|
|
|
|
if fragment:
|
|
|
|
url += '#' + fragment
|
|
|
|
self.assertEqual(url, expected_url,
|
|
|
|
"Response redirected to '%s', expected '%s'" % (url, expected_url))
|
|
|
|
|
|
|
|
redirect_response = self.client.get(path, QueryDict(query))
|
2007-05-10 19:27:59 +08:00
|
|
|
self.assertEqual(redirect_response.status_code, target_status_code,
|
|
|
|
"Couldn't retrieve redirection page '%s': response code was %d (expected %d)" %
|
2007-05-14 19:07:14 +08:00
|
|
|
(path, redirect_response.status_code, target_status_code))
|
2007-05-05 11:03:33 +08:00
|
|
|
|
2007-07-20 22:32:20 +08:00
|
|
|
def assertContains(self, response, text, count=None, status_code=200):
|
2007-05-05 11:03:33 +08:00
|
|
|
"""Assert that a response indicates that a page was retreived successfully,
|
2007-05-10 19:27:59 +08:00
|
|
|
(i.e., the HTTP status code was as expected), and that ``text`` occurs ``count``
|
2007-07-20 22:32:20 +08:00
|
|
|
times in the content of the response. If ``count`` is None, the count doesn't
|
|
|
|
matter - the assertion is true if the text occurs at least once in the response.
|
2007-05-05 11:03:33 +08:00
|
|
|
|
|
|
|
"""
|
2007-05-10 19:27:59 +08:00
|
|
|
self.assertEqual(response.status_code, status_code,
|
|
|
|
"Couldn't retrieve page: Response code was %d (expected %d)'" %
|
|
|
|
(response.status_code, status_code))
|
2007-05-05 11:03:33 +08:00
|
|
|
real_count = response.content.count(text)
|
2007-07-21 12:36:28 +08:00
|
|
|
if count is not None:
|
2007-07-20 22:32:20 +08:00
|
|
|
self.assertEqual(real_count, count,
|
|
|
|
"Found %d instances of '%s' in response (expected %d)" % (real_count, text, count))
|
|
|
|
else:
|
2007-08-12 10:14:52 +08:00
|
|
|
self.failUnless(real_count != 0, "Couldn't find '%s' in response" % text)
|
2007-07-20 22:32:20 +08:00
|
|
|
|
2007-05-07 20:34:18 +08:00
|
|
|
def assertFormError(self, response, form, field, errors):
|
|
|
|
"Assert that a form used to render the response has a specific field error"
|
|
|
|
if not response.context:
|
|
|
|
self.fail('Response did not use any contexts to render the response')
|
|
|
|
|
|
|
|
# If there is a single context, put it into a list to simplify processing
|
|
|
|
if not isinstance(response.context, list):
|
|
|
|
contexts = [response.context]
|
|
|
|
else:
|
|
|
|
contexts = response.context
|
|
|
|
|
|
|
|
# If a single error string is provided, make it a list to simplify processing
|
|
|
|
if not isinstance(errors, list):
|
|
|
|
errors = [errors]
|
|
|
|
|
|
|
|
# Search all contexts for the error.
|
|
|
|
found_form = False
|
|
|
|
for i,context in enumerate(contexts):
|
|
|
|
if form in context:
|
|
|
|
found_form = True
|
2007-05-10 21:48:18 +08:00
|
|
|
for err in errors:
|
|
|
|
if field:
|
|
|
|
if field in context[form].errors:
|
2007-05-13 00:53:27 +08:00
|
|
|
self.failUnless(err in context[form].errors[field],
|
2007-05-10 21:48:18 +08:00
|
|
|
"The field '%s' on form '%s' in context %d does not contain the error '%s' (actual errors: %s)" %
|
|
|
|
(field, form, i, err, list(context[form].errors[field])))
|
|
|
|
elif field in context[form].fields:
|
|
|
|
self.fail("The field '%s' on form '%s' in context %d contains no errors" %
|
|
|
|
(field, form, i))
|
2007-05-07 20:34:18 +08:00
|
|
|
else:
|
2007-05-10 21:48:18 +08:00
|
|
|
self.fail("The form '%s' in context %d does not contain the field '%s'" % (form, i, field))
|
|
|
|
else:
|
2007-05-13 00:53:27 +08:00
|
|
|
self.failUnless(err in context[form].non_field_errors(),
|
2007-05-10 21:48:18 +08:00
|
|
|
"The form '%s' in context %d does not contain the non-field error '%s' (actual errors: %s)" %
|
|
|
|
(form, i, err, list(context[form].non_field_errors())))
|
2007-05-07 20:34:18 +08:00
|
|
|
if not found_form:
|
|
|
|
self.fail("The form '%s' was not used to render the response" % form)
|
|
|
|
|
|
|
|
def assertTemplateUsed(self, response, template_name):
|
|
|
|
"Assert that the template with the provided name was used in rendering the response"
|
|
|
|
if isinstance(response.template, list):
|
|
|
|
template_names = [t.name for t in response.template]
|
2007-05-13 00:53:27 +08:00
|
|
|
self.failUnless(template_name in template_names,
|
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 20:11:04 +08:00
|
|
|
u"Template '%s' was not one of the templates used to render the response. Templates used: %s" %
|
|
|
|
(template_name, u', '.join(template_names)))
|
2007-05-07 20:34:18 +08:00
|
|
|
elif response.template:
|
|
|
|
self.assertEqual(template_name, response.template.name,
|
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 20:11:04 +08:00
|
|
|
u"Template '%s' was not used to render the response. Actual template was '%s'" %
|
2007-05-07 20:34:18 +08:00
|
|
|
(template_name, response.template.name))
|
|
|
|
else:
|
|
|
|
self.fail('No templates used to render the response')
|
|
|
|
|
|
|
|
def assertTemplateNotUsed(self, response, template_name):
|
|
|
|
"Assert that the template with the provided name was NOT used in rendering the response"
|
|
|
|
if isinstance(response.template, list):
|
2007-05-13 00:53:27 +08:00
|
|
|
self.failIf(template_name in [t.name for t in response.template],
|
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 20:11:04 +08:00
|
|
|
u"Template '%s' was used unexpectedly in rendering the response" % template_name)
|
2007-05-07 20:34:18 +08:00
|
|
|
elif response.template:
|
|
|
|
self.assertNotEqual(template_name, response.template.name,
|
Merged Unicode branch into trunk (r4952:5608). This should be fully
backwards compatible for all practical purposes.
Fixed #2391, #2489, #2996, #3322, #3344, #3370, #3406, #3432, #3454, #3492, #3582, #3690, #3878, #3891, #3937, #4039, #4141, #4227, #4286, #4291, #4300, #4452, #4702
git-svn-id: http://code.djangoproject.com/svn/django/trunk@5609 bcc190cf-cafb-0310-a4f2-bffc1f526a37
2007-07-04 20:11:04 +08:00
|
|
|
u"Template '%s' was used unexpectedly in rendering the response" % template_name)
|
|
|
|
|