mirror of https://github.com/django/django.git
Fixed #20004 -- Moved non DB-related assertions to SimpleTestCase.
Thanks zalew for the suggestion and work on a patch. Also updated, tweaked and fixed testing documentation.
This commit is contained in:
parent
69523c1ba3
commit
0a50311063
|
@ -231,6 +231,10 @@ class _AssertTemplateNotUsedContext(_AssertTemplateUsedContext):
|
||||||
|
|
||||||
class SimpleTestCase(ut2.TestCase):
|
class SimpleTestCase(ut2.TestCase):
|
||||||
|
|
||||||
|
# The class we'll use for the test client self.client.
|
||||||
|
# Can be overridden in derived classes.
|
||||||
|
client_class = Client
|
||||||
|
|
||||||
_warn_txt = ("save_warnings_state/restore_warnings_state "
|
_warn_txt = ("save_warnings_state/restore_warnings_state "
|
||||||
"django.test.*TestCase methods are deprecated. Use Python's "
|
"django.test.*TestCase methods are deprecated. Use Python's "
|
||||||
"warnings.catch_warnings context manager instead.")
|
"warnings.catch_warnings context manager instead.")
|
||||||
|
@ -264,10 +268,31 @@ class SimpleTestCase(ut2.TestCase):
|
||||||
return
|
return
|
||||||
|
|
||||||
def _pre_setup(self):
|
def _pre_setup(self):
|
||||||
pass
|
"""Performs any pre-test setup. This includes:
|
||||||
|
|
||||||
|
* If the Test Case class has a 'urls' member, replace the
|
||||||
|
ROOT_URLCONF with it.
|
||||||
|
* Clearing the mail test outbox.
|
||||||
|
"""
|
||||||
|
self.client = self.client_class()
|
||||||
|
self._urlconf_setup()
|
||||||
|
mail.outbox = []
|
||||||
|
|
||||||
|
def _urlconf_setup(self):
|
||||||
|
set_urlconf(None)
|
||||||
|
if hasattr(self, 'urls'):
|
||||||
|
self._old_root_urlconf = settings.ROOT_URLCONF
|
||||||
|
settings.ROOT_URLCONF = self.urls
|
||||||
|
clear_url_caches()
|
||||||
|
|
||||||
def _post_teardown(self):
|
def _post_teardown(self):
|
||||||
pass
|
self._urlconf_teardown()
|
||||||
|
|
||||||
|
def _urlconf_teardown(self):
|
||||||
|
set_urlconf(None)
|
||||||
|
if hasattr(self, '_old_root_urlconf'):
|
||||||
|
settings.ROOT_URLCONF = self._old_root_urlconf
|
||||||
|
clear_url_caches()
|
||||||
|
|
||||||
def save_warnings_state(self):
|
def save_warnings_state(self):
|
||||||
"""
|
"""
|
||||||
|
@ -291,258 +316,6 @@ class SimpleTestCase(ut2.TestCase):
|
||||||
"""
|
"""
|
||||||
return override_settings(**kwargs)
|
return override_settings(**kwargs)
|
||||||
|
|
||||||
def assertRaisesMessage(self, expected_exception, expected_message,
|
|
||||||
callable_obj=None, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Asserts that the message in a raised exception matches the passed
|
|
||||||
value.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
expected_exception: Exception class expected to be raised.
|
|
||||||
expected_message: expected error message string value.
|
|
||||||
callable_obj: Function to be called.
|
|
||||||
args: Extra args.
|
|
||||||
kwargs: Extra kwargs.
|
|
||||||
"""
|
|
||||||
return six.assertRaisesRegex(self, expected_exception,
|
|
||||||
re.escape(expected_message), callable_obj, *args, **kwargs)
|
|
||||||
|
|
||||||
def assertFieldOutput(self, fieldclass, valid, invalid, field_args=None,
|
|
||||||
field_kwargs=None, empty_value=''):
|
|
||||||
"""
|
|
||||||
Asserts that a form field behaves correctly with various inputs.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
fieldclass: the class of the field to be tested.
|
|
||||||
valid: a dictionary mapping valid inputs to their expected
|
|
||||||
cleaned values.
|
|
||||||
invalid: a dictionary mapping invalid inputs to one or more
|
|
||||||
raised error messages.
|
|
||||||
field_args: the args passed to instantiate the field
|
|
||||||
field_kwargs: the kwargs passed to instantiate the field
|
|
||||||
empty_value: the expected clean output for inputs in empty_values
|
|
||||||
|
|
||||||
"""
|
|
||||||
if field_args is None:
|
|
||||||
field_args = []
|
|
||||||
if field_kwargs is None:
|
|
||||||
field_kwargs = {}
|
|
||||||
required = fieldclass(*field_args, **field_kwargs)
|
|
||||||
optional = fieldclass(*field_args,
|
|
||||||
**dict(field_kwargs, required=False))
|
|
||||||
# test valid inputs
|
|
||||||
for input, output in valid.items():
|
|
||||||
self.assertEqual(required.clean(input), output)
|
|
||||||
self.assertEqual(optional.clean(input), output)
|
|
||||||
# test invalid inputs
|
|
||||||
for input, errors in invalid.items():
|
|
||||||
with self.assertRaises(ValidationError) as context_manager:
|
|
||||||
required.clean(input)
|
|
||||||
self.assertEqual(context_manager.exception.messages, errors)
|
|
||||||
|
|
||||||
with self.assertRaises(ValidationError) as context_manager:
|
|
||||||
optional.clean(input)
|
|
||||||
self.assertEqual(context_manager.exception.messages, errors)
|
|
||||||
# test required inputs
|
|
||||||
error_required = [force_text(required.error_messages['required'])]
|
|
||||||
for e in required.empty_values:
|
|
||||||
with self.assertRaises(ValidationError) as context_manager:
|
|
||||||
required.clean(e)
|
|
||||||
self.assertEqual(context_manager.exception.messages,
|
|
||||||
error_required)
|
|
||||||
self.assertEqual(optional.clean(e), empty_value)
|
|
||||||
# test that max_length and min_length are always accepted
|
|
||||||
if issubclass(fieldclass, CharField):
|
|
||||||
field_kwargs.update({'min_length':2, 'max_length':20})
|
|
||||||
self.assertTrue(isinstance(fieldclass(*field_args, **field_kwargs),
|
|
||||||
fieldclass))
|
|
||||||
|
|
||||||
def assertHTMLEqual(self, html1, html2, msg=None):
|
|
||||||
"""
|
|
||||||
Asserts that two HTML snippets are semantically the same.
|
|
||||||
Whitespace in most cases is ignored, and attribute ordering is not
|
|
||||||
significant. The passed-in arguments must be valid HTML.
|
|
||||||
"""
|
|
||||||
dom1 = assert_and_parse_html(self, html1, msg,
|
|
||||||
'First argument is not valid HTML:')
|
|
||||||
dom2 = assert_and_parse_html(self, html2, msg,
|
|
||||||
'Second argument is not valid HTML:')
|
|
||||||
|
|
||||||
if dom1 != dom2:
|
|
||||||
standardMsg = '%s != %s' % (
|
|
||||||
safe_repr(dom1, True), safe_repr(dom2, True))
|
|
||||||
diff = ('\n' + '\n'.join(difflib.ndiff(
|
|
||||||
six.text_type(dom1).splitlines(),
|
|
||||||
six.text_type(dom2).splitlines())))
|
|
||||||
standardMsg = self._truncateMessage(standardMsg, diff)
|
|
||||||
self.fail(self._formatMessage(msg, standardMsg))
|
|
||||||
|
|
||||||
def assertHTMLNotEqual(self, html1, html2, msg=None):
|
|
||||||
"""Asserts that two HTML snippets are not semantically equivalent."""
|
|
||||||
dom1 = assert_and_parse_html(self, html1, msg,
|
|
||||||
'First argument is not valid HTML:')
|
|
||||||
dom2 = assert_and_parse_html(self, html2, msg,
|
|
||||||
'Second argument is not valid HTML:')
|
|
||||||
|
|
||||||
if dom1 == dom2:
|
|
||||||
standardMsg = '%s == %s' % (
|
|
||||||
safe_repr(dom1, True), safe_repr(dom2, True))
|
|
||||||
self.fail(self._formatMessage(msg, standardMsg))
|
|
||||||
|
|
||||||
def assertInHTML(self, needle, haystack, count = None, msg_prefix=''):
|
|
||||||
needle = assert_and_parse_html(self, needle, None,
|
|
||||||
'First argument is not valid HTML:')
|
|
||||||
haystack = assert_and_parse_html(self, haystack, None,
|
|
||||||
'Second argument is not valid HTML:')
|
|
||||||
real_count = haystack.count(needle)
|
|
||||||
if count is not None:
|
|
||||||
self.assertEqual(real_count, count,
|
|
||||||
msg_prefix + "Found %d instances of '%s' in response"
|
|
||||||
" (expected %d)" % (real_count, needle, count))
|
|
||||||
else:
|
|
||||||
self.assertTrue(real_count != 0,
|
|
||||||
msg_prefix + "Couldn't find '%s' in response" % needle)
|
|
||||||
|
|
||||||
def assertJSONEqual(self, raw, expected_data, msg=None):
|
|
||||||
try:
|
|
||||||
data = json.loads(raw)
|
|
||||||
except ValueError:
|
|
||||||
self.fail("First argument is not valid JSON: %r" % raw)
|
|
||||||
if isinstance(expected_data, six.string_types):
|
|
||||||
try:
|
|
||||||
expected_data = json.loads(expected_data)
|
|
||||||
except ValueError:
|
|
||||||
self.fail("Second argument is not valid JSON: %r" % expected_data)
|
|
||||||
self.assertEqual(data, expected_data, msg=msg)
|
|
||||||
|
|
||||||
def assertXMLEqual(self, xml1, xml2, msg=None):
|
|
||||||
"""
|
|
||||||
Asserts that two XML snippets are semantically the same.
|
|
||||||
Whitespace in most cases is ignored, and attribute ordering is not
|
|
||||||
significant. The passed-in arguments must be valid XML.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
result = compare_xml(xml1, xml2)
|
|
||||||
except Exception as e:
|
|
||||||
standardMsg = 'First or second argument is not valid XML\n%s' % e
|
|
||||||
self.fail(self._formatMessage(msg, standardMsg))
|
|
||||||
else:
|
|
||||||
if not result:
|
|
||||||
standardMsg = '%s != %s' % (safe_repr(xml1, True), safe_repr(xml2, True))
|
|
||||||
self.fail(self._formatMessage(msg, standardMsg))
|
|
||||||
|
|
||||||
def assertXMLNotEqual(self, xml1, xml2, msg=None):
|
|
||||||
"""
|
|
||||||
Asserts that two XML snippets are not semantically equivalent.
|
|
||||||
Whitespace in most cases is ignored, and attribute ordering is not
|
|
||||||
significant. The passed-in arguments must be valid XML.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
result = compare_xml(xml1, xml2)
|
|
||||||
except Exception as e:
|
|
||||||
standardMsg = 'First or second argument is not valid XML\n%s' % e
|
|
||||||
self.fail(self._formatMessage(msg, standardMsg))
|
|
||||||
else:
|
|
||||||
if result:
|
|
||||||
standardMsg = '%s == %s' % (safe_repr(xml1, True), safe_repr(xml2, True))
|
|
||||||
self.fail(self._formatMessage(msg, standardMsg))
|
|
||||||
|
|
||||||
|
|
||||||
class TransactionTestCase(SimpleTestCase):
|
|
||||||
|
|
||||||
# The class we'll use for the test client self.client.
|
|
||||||
# Can be overridden in derived classes.
|
|
||||||
client_class = Client
|
|
||||||
|
|
||||||
# Subclasses can ask for resetting of auto increment sequence before each
|
|
||||||
# test case
|
|
||||||
reset_sequences = False
|
|
||||||
|
|
||||||
def _pre_setup(self):
|
|
||||||
"""Performs any pre-test setup. This includes:
|
|
||||||
|
|
||||||
* Flushing the database.
|
|
||||||
* If the Test Case class has a 'fixtures' member, installing the
|
|
||||||
named fixtures.
|
|
||||||
* If the Test Case class has a 'urls' member, replace the
|
|
||||||
ROOT_URLCONF with it.
|
|
||||||
* Clearing the mail test outbox.
|
|
||||||
"""
|
|
||||||
self.client = self.client_class()
|
|
||||||
self._fixture_setup()
|
|
||||||
self._urlconf_setup()
|
|
||||||
mail.outbox = []
|
|
||||||
|
|
||||||
def _databases_names(self, include_mirrors=True):
|
|
||||||
# If the test case has a multi_db=True flag, act on all databases,
|
|
||||||
# including mirrors or not. Otherwise, just on the default DB.
|
|
||||||
if getattr(self, 'multi_db', False):
|
|
||||||
return [alias for alias in connections
|
|
||||||
if include_mirrors or not connections[alias].settings_dict['TEST_MIRROR']]
|
|
||||||
else:
|
|
||||||
return [DEFAULT_DB_ALIAS]
|
|
||||||
|
|
||||||
def _reset_sequences(self, db_name):
|
|
||||||
conn = connections[db_name]
|
|
||||||
if conn.features.supports_sequence_reset:
|
|
||||||
sql_list = \
|
|
||||||
conn.ops.sequence_reset_by_name_sql(no_style(),
|
|
||||||
conn.introspection.sequence_list())
|
|
||||||
if sql_list:
|
|
||||||
with transaction.commit_on_success_unless_managed(using=db_name):
|
|
||||||
cursor = conn.cursor()
|
|
||||||
for sql in sql_list:
|
|
||||||
cursor.execute(sql)
|
|
||||||
|
|
||||||
def _fixture_setup(self):
|
|
||||||
for db_name in self._databases_names(include_mirrors=False):
|
|
||||||
# Reset sequences
|
|
||||||
if self.reset_sequences:
|
|
||||||
self._reset_sequences(db_name)
|
|
||||||
|
|
||||||
if hasattr(self, 'fixtures'):
|
|
||||||
# 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, 'database': db_name, 'skip_validation': True})
|
|
||||||
|
|
||||||
def _urlconf_setup(self):
|
|
||||||
set_urlconf(None)
|
|
||||||
if hasattr(self, 'urls'):
|
|
||||||
self._old_root_urlconf = settings.ROOT_URLCONF
|
|
||||||
settings.ROOT_URLCONF = self.urls
|
|
||||||
clear_url_caches()
|
|
||||||
|
|
||||||
def _post_teardown(self):
|
|
||||||
""" Performs any post-test things. This includes:
|
|
||||||
|
|
||||||
* Putting back the original ROOT_URLCONF if it was changed.
|
|
||||||
* Force closing the connection, so that the next test gets
|
|
||||||
a clean cursor.
|
|
||||||
"""
|
|
||||||
self._fixture_teardown()
|
|
||||||
self._urlconf_teardown()
|
|
||||||
# Some DB cursors include SQL statements as part of cursor
|
|
||||||
# creation. If you have a test that does rollback, the effect
|
|
||||||
# of these statements is lost, which can effect the operation
|
|
||||||
# of tests (e.g., losing a timezone setting causing objects to
|
|
||||||
# be created with the wrong time).
|
|
||||||
# To make sure this doesn't happen, get a clean connection at the
|
|
||||||
# start of every test.
|
|
||||||
for conn in connections.all():
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
def _fixture_teardown(self):
|
|
||||||
for db in self._databases_names(include_mirrors=False):
|
|
||||||
call_command('flush', verbosity=0, interactive=False, database=db,
|
|
||||||
skip_validation=True, reset_sequences=False)
|
|
||||||
|
|
||||||
def _urlconf_teardown(self):
|
|
||||||
set_urlconf(None)
|
|
||||||
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, msg_prefix=''):
|
target_status_code=200, host=None, msg_prefix=''):
|
||||||
"""Asserts that a response redirected to a specific URL, and that the
|
"""Asserts that a response redirected to a specific URL, and that the
|
||||||
|
@ -787,6 +560,236 @@ class TransactionTestCase(SimpleTestCase):
|
||||||
msg_prefix + "Template '%s' was used unexpectedly in rendering"
|
msg_prefix + "Template '%s' was used unexpectedly in rendering"
|
||||||
" the response" % template_name)
|
" the response" % template_name)
|
||||||
|
|
||||||
|
def assertRaisesMessage(self, expected_exception, expected_message,
|
||||||
|
callable_obj=None, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Asserts that the message in a raised exception matches the passed
|
||||||
|
value.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
expected_exception: Exception class expected to be raised.
|
||||||
|
expected_message: expected error message string value.
|
||||||
|
callable_obj: Function to be called.
|
||||||
|
args: Extra args.
|
||||||
|
kwargs: Extra kwargs.
|
||||||
|
"""
|
||||||
|
return six.assertRaisesRegex(self, expected_exception,
|
||||||
|
re.escape(expected_message), callable_obj, *args, **kwargs)
|
||||||
|
|
||||||
|
def assertFieldOutput(self, fieldclass, valid, invalid, field_args=None,
|
||||||
|
field_kwargs=None, empty_value=''):
|
||||||
|
"""
|
||||||
|
Asserts that a form field behaves correctly with various inputs.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
fieldclass: the class of the field to be tested.
|
||||||
|
valid: a dictionary mapping valid inputs to their expected
|
||||||
|
cleaned values.
|
||||||
|
invalid: a dictionary mapping invalid inputs to one or more
|
||||||
|
raised error messages.
|
||||||
|
field_args: the args passed to instantiate the field
|
||||||
|
field_kwargs: the kwargs passed to instantiate the field
|
||||||
|
empty_value: the expected clean output for inputs in empty_values
|
||||||
|
|
||||||
|
"""
|
||||||
|
if field_args is None:
|
||||||
|
field_args = []
|
||||||
|
if field_kwargs is None:
|
||||||
|
field_kwargs = {}
|
||||||
|
required = fieldclass(*field_args, **field_kwargs)
|
||||||
|
optional = fieldclass(*field_args,
|
||||||
|
**dict(field_kwargs, required=False))
|
||||||
|
# test valid inputs
|
||||||
|
for input, output in valid.items():
|
||||||
|
self.assertEqual(required.clean(input), output)
|
||||||
|
self.assertEqual(optional.clean(input), output)
|
||||||
|
# test invalid inputs
|
||||||
|
for input, errors in invalid.items():
|
||||||
|
with self.assertRaises(ValidationError) as context_manager:
|
||||||
|
required.clean(input)
|
||||||
|
self.assertEqual(context_manager.exception.messages, errors)
|
||||||
|
|
||||||
|
with self.assertRaises(ValidationError) as context_manager:
|
||||||
|
optional.clean(input)
|
||||||
|
self.assertEqual(context_manager.exception.messages, errors)
|
||||||
|
# test required inputs
|
||||||
|
error_required = [force_text(required.error_messages['required'])]
|
||||||
|
for e in required.empty_values:
|
||||||
|
with self.assertRaises(ValidationError) as context_manager:
|
||||||
|
required.clean(e)
|
||||||
|
self.assertEqual(context_manager.exception.messages,
|
||||||
|
error_required)
|
||||||
|
self.assertEqual(optional.clean(e), empty_value)
|
||||||
|
# test that max_length and min_length are always accepted
|
||||||
|
if issubclass(fieldclass, CharField):
|
||||||
|
field_kwargs.update({'min_length':2, 'max_length':20})
|
||||||
|
self.assertTrue(isinstance(fieldclass(*field_args, **field_kwargs),
|
||||||
|
fieldclass))
|
||||||
|
|
||||||
|
def assertHTMLEqual(self, html1, html2, msg=None):
|
||||||
|
"""
|
||||||
|
Asserts that two HTML snippets are semantically the same.
|
||||||
|
Whitespace in most cases is ignored, and attribute ordering is not
|
||||||
|
significant. The passed-in arguments must be valid HTML.
|
||||||
|
"""
|
||||||
|
dom1 = assert_and_parse_html(self, html1, msg,
|
||||||
|
'First argument is not valid HTML:')
|
||||||
|
dom2 = assert_and_parse_html(self, html2, msg,
|
||||||
|
'Second argument is not valid HTML:')
|
||||||
|
|
||||||
|
if dom1 != dom2:
|
||||||
|
standardMsg = '%s != %s' % (
|
||||||
|
safe_repr(dom1, True), safe_repr(dom2, True))
|
||||||
|
diff = ('\n' + '\n'.join(difflib.ndiff(
|
||||||
|
six.text_type(dom1).splitlines(),
|
||||||
|
six.text_type(dom2).splitlines())))
|
||||||
|
standardMsg = self._truncateMessage(standardMsg, diff)
|
||||||
|
self.fail(self._formatMessage(msg, standardMsg))
|
||||||
|
|
||||||
|
def assertHTMLNotEqual(self, html1, html2, msg=None):
|
||||||
|
"""Asserts that two HTML snippets are not semantically equivalent."""
|
||||||
|
dom1 = assert_and_parse_html(self, html1, msg,
|
||||||
|
'First argument is not valid HTML:')
|
||||||
|
dom2 = assert_and_parse_html(self, html2, msg,
|
||||||
|
'Second argument is not valid HTML:')
|
||||||
|
|
||||||
|
if dom1 == dom2:
|
||||||
|
standardMsg = '%s == %s' % (
|
||||||
|
safe_repr(dom1, True), safe_repr(dom2, True))
|
||||||
|
self.fail(self._formatMessage(msg, standardMsg))
|
||||||
|
|
||||||
|
def assertInHTML(self, needle, haystack, count=None, msg_prefix=''):
|
||||||
|
needle = assert_and_parse_html(self, needle, None,
|
||||||
|
'First argument is not valid HTML:')
|
||||||
|
haystack = assert_and_parse_html(self, haystack, None,
|
||||||
|
'Second argument is not valid HTML:')
|
||||||
|
real_count = haystack.count(needle)
|
||||||
|
if count is not None:
|
||||||
|
self.assertEqual(real_count, count,
|
||||||
|
msg_prefix + "Found %d instances of '%s' in response"
|
||||||
|
" (expected %d)" % (real_count, needle, count))
|
||||||
|
else:
|
||||||
|
self.assertTrue(real_count != 0,
|
||||||
|
msg_prefix + "Couldn't find '%s' in response" % needle)
|
||||||
|
|
||||||
|
def assertJSONEqual(self, raw, expected_data, msg=None):
|
||||||
|
try:
|
||||||
|
data = json.loads(raw)
|
||||||
|
except ValueError:
|
||||||
|
self.fail("First argument is not valid JSON: %r" % raw)
|
||||||
|
if isinstance(expected_data, six.string_types):
|
||||||
|
try:
|
||||||
|
expected_data = json.loads(expected_data)
|
||||||
|
except ValueError:
|
||||||
|
self.fail("Second argument is not valid JSON: %r" % expected_data)
|
||||||
|
self.assertEqual(data, expected_data, msg=msg)
|
||||||
|
|
||||||
|
def assertXMLEqual(self, xml1, xml2, msg=None):
|
||||||
|
"""
|
||||||
|
Asserts that two XML snippets are semantically the same.
|
||||||
|
Whitespace in most cases is ignored, and attribute ordering is not
|
||||||
|
significant. The passed-in arguments must be valid XML.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
result = compare_xml(xml1, xml2)
|
||||||
|
except Exception as e:
|
||||||
|
standardMsg = 'First or second argument is not valid XML\n%s' % e
|
||||||
|
self.fail(self._formatMessage(msg, standardMsg))
|
||||||
|
else:
|
||||||
|
if not result:
|
||||||
|
standardMsg = '%s != %s' % (safe_repr(xml1, True), safe_repr(xml2, True))
|
||||||
|
self.fail(self._formatMessage(msg, standardMsg))
|
||||||
|
|
||||||
|
def assertXMLNotEqual(self, xml1, xml2, msg=None):
|
||||||
|
"""
|
||||||
|
Asserts that two XML snippets are not semantically equivalent.
|
||||||
|
Whitespace in most cases is ignored, and attribute ordering is not
|
||||||
|
significant. The passed-in arguments must be valid XML.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
result = compare_xml(xml1, xml2)
|
||||||
|
except Exception as e:
|
||||||
|
standardMsg = 'First or second argument is not valid XML\n%s' % e
|
||||||
|
self.fail(self._formatMessage(msg, standardMsg))
|
||||||
|
else:
|
||||||
|
if result:
|
||||||
|
standardMsg = '%s == %s' % (safe_repr(xml1, True), safe_repr(xml2, True))
|
||||||
|
self.fail(self._formatMessage(msg, standardMsg))
|
||||||
|
|
||||||
|
|
||||||
|
class TransactionTestCase(SimpleTestCase):
|
||||||
|
|
||||||
|
# Subclasses can ask for resetting of auto increment sequence before each
|
||||||
|
# test case
|
||||||
|
reset_sequences = False
|
||||||
|
|
||||||
|
def _pre_setup(self):
|
||||||
|
"""Performs any pre-test setup. This includes:
|
||||||
|
|
||||||
|
* Flushing the database.
|
||||||
|
* If the Test Case class has a 'fixtures' member, installing the
|
||||||
|
named fixtures.
|
||||||
|
"""
|
||||||
|
super(TransactionTestCase, self)._pre_setup()
|
||||||
|
self._fixture_setup()
|
||||||
|
|
||||||
|
def _databases_names(self, include_mirrors=True):
|
||||||
|
# If the test case has a multi_db=True flag, act on all databases,
|
||||||
|
# including mirrors or not. Otherwise, just on the default DB.
|
||||||
|
if getattr(self, 'multi_db', False):
|
||||||
|
return [alias for alias in connections
|
||||||
|
if include_mirrors or not connections[alias].settings_dict['TEST_MIRROR']]
|
||||||
|
else:
|
||||||
|
return [DEFAULT_DB_ALIAS]
|
||||||
|
|
||||||
|
def _reset_sequences(self, db_name):
|
||||||
|
conn = connections[db_name]
|
||||||
|
if conn.features.supports_sequence_reset:
|
||||||
|
sql_list = \
|
||||||
|
conn.ops.sequence_reset_by_name_sql(no_style(),
|
||||||
|
conn.introspection.sequence_list())
|
||||||
|
if sql_list:
|
||||||
|
with transaction.commit_on_success_unless_managed(using=db_name):
|
||||||
|
cursor = conn.cursor()
|
||||||
|
for sql in sql_list:
|
||||||
|
cursor.execute(sql)
|
||||||
|
|
||||||
|
def _fixture_setup(self):
|
||||||
|
for db_name in self._databases_names(include_mirrors=False):
|
||||||
|
# Reset sequences
|
||||||
|
if self.reset_sequences:
|
||||||
|
self._reset_sequences(db_name)
|
||||||
|
|
||||||
|
if hasattr(self, 'fixtures'):
|
||||||
|
# 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, 'database': db_name, 'skip_validation': True})
|
||||||
|
|
||||||
|
def _post_teardown(self):
|
||||||
|
"""Performs any post-test things. This includes:
|
||||||
|
|
||||||
|
* Putting back the original ROOT_URLCONF if it was changed.
|
||||||
|
* Force closing the connection, so that the next test gets
|
||||||
|
a clean cursor.
|
||||||
|
"""
|
||||||
|
self._fixture_teardown()
|
||||||
|
super(TransactionTestCase, self)._post_teardown()
|
||||||
|
# Some DB cursors include SQL statements as part of cursor
|
||||||
|
# creation. If you have a test that does rollback, the effect
|
||||||
|
# of these statements is lost, which can effect the operation
|
||||||
|
# of tests (e.g., losing a timezone setting causing objects to
|
||||||
|
# be created with the wrong time).
|
||||||
|
# To make sure this doesn't happen, get a clean connection at the
|
||||||
|
# start of every test.
|
||||||
|
for conn in connections.all():
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
def _fixture_teardown(self):
|
||||||
|
for db_name in self._databases_names(include_mirrors=False):
|
||||||
|
call_command('flush', verbosity=0, interactive=False, database=db_name,
|
||||||
|
skip_validation=True, reset_sequences=False)
|
||||||
|
|
||||||
def assertQuerysetEqual(self, qs, values, transform=repr, ordered=True):
|
def assertQuerysetEqual(self, qs, values, transform=repr, ordered=True):
|
||||||
items = six.moves.map(transform, qs)
|
items = six.moves.map(transform, qs)
|
||||||
if not ordered:
|
if not ordered:
|
||||||
|
@ -841,14 +844,14 @@ class TestCase(TransactionTestCase):
|
||||||
# Remove this when the legacy transaction management goes away.
|
# Remove this when the legacy transaction management goes away.
|
||||||
disable_transaction_methods()
|
disable_transaction_methods()
|
||||||
|
|
||||||
for db in self._databases_names(include_mirrors=False):
|
for db_name in self._databases_names(include_mirrors=False):
|
||||||
if hasattr(self, 'fixtures'):
|
if hasattr(self, 'fixtures'):
|
||||||
try:
|
try:
|
||||||
call_command('loaddata', *self.fixtures,
|
call_command('loaddata', *self.fixtures,
|
||||||
**{
|
**{
|
||||||
'verbosity': 0,
|
'verbosity': 0,
|
||||||
'commit': False,
|
'commit': False,
|
||||||
'database': db,
|
'database': db_name,
|
||||||
'skip_validation': True,
|
'skip_validation': True,
|
||||||
})
|
})
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
|
@ -503,8 +503,8 @@ of the process of creating polls.
|
||||||
message: "No polls are available." and verifies the ``latest_poll_list`` is
|
message: "No polls are available." and verifies the ``latest_poll_list`` is
|
||||||
empty. Note that the :class:`django.test.TestCase` class provides some
|
empty. Note that the :class:`django.test.TestCase` class provides some
|
||||||
additional assertion methods. In these examples, we use
|
additional assertion methods. In these examples, we use
|
||||||
:meth:`~django.test.TestCase.assertContains()` and
|
:meth:`~django.test.SimpleTestCase.assertContains()` and
|
||||||
:meth:`~django.test.TestCase.assertQuerysetEqual()`.
|
:meth:`~django.test.TransactionTestCase.assertQuerysetEqual()`.
|
||||||
|
|
||||||
In ``test_index_view_with_a_past_poll``, we create a poll and verify that it
|
In ``test_index_view_with_a_past_poll``, we create a poll and verify that it
|
||||||
appears in the list.
|
appears in the list.
|
||||||
|
|
|
@ -329,7 +329,7 @@ model:
|
||||||
.. admonition:: Serializing references to ``ContentType`` objects
|
.. admonition:: Serializing references to ``ContentType`` objects
|
||||||
|
|
||||||
If you're serializing data (for example, when generating
|
If you're serializing data (for example, when generating
|
||||||
:class:`~django.test.TestCase.fixtures`) from a model that implements
|
:class:`~django.test.TransactionTestCase.fixtures`) from a model that implements
|
||||||
generic relations, you should probably be using a natural key to uniquely
|
generic relations, you should probably be using a natural key to uniquely
|
||||||
identify related :class:`~django.contrib.contenttypes.models.ContentType`
|
identify related :class:`~django.contrib.contenttypes.models.ContentType`
|
||||||
objects. See :ref:`natural keys<topics-serialization-natural-keys>` and
|
objects. See :ref:`natural keys<topics-serialization-natural-keys>` and
|
||||||
|
|
|
@ -154,7 +154,7 @@ requests. These include:
|
||||||
requests in tests.
|
requests in tests.
|
||||||
|
|
||||||
* A new test assertion --
|
* A new test assertion --
|
||||||
:meth:`~django.test.TestCase.assertNumQueries` -- making it
|
:meth:`~django.test.TransactionTestCase.assertNumQueries` -- making it
|
||||||
easier to test the database activity associated with a view.
|
easier to test the database activity associated with a view.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -299,7 +299,7 @@ requests. These include:
|
||||||
in tests.
|
in tests.
|
||||||
|
|
||||||
* A new test assertion --
|
* A new test assertion --
|
||||||
:meth:`~django.test.TestCase.assertNumQueries` -- making it
|
:meth:`~django.test.TransactionTestCase.assertNumQueries` -- making it
|
||||||
easier to test the database activity associated with a view.
|
easier to test the database activity associated with a view.
|
||||||
|
|
||||||
* Support for lookups spanning relations in admin's
|
* Support for lookups spanning relations in admin's
|
||||||
|
|
|
@ -541,8 +541,8 @@ compare HTML directly with the new
|
||||||
:meth:`~django.test.SimpleTestCase.assertHTMLEqual` and
|
:meth:`~django.test.SimpleTestCase.assertHTMLEqual` and
|
||||||
:meth:`~django.test.SimpleTestCase.assertHTMLNotEqual` assertions, or use
|
:meth:`~django.test.SimpleTestCase.assertHTMLNotEqual` assertions, or use
|
||||||
the ``html=True`` flag with
|
the ``html=True`` flag with
|
||||||
:meth:`~django.test.TestCase.assertContains` and
|
:meth:`~django.test.SimpleTestCase.assertContains` and
|
||||||
:meth:`~django.test.TestCase.assertNotContains` to test whether the
|
:meth:`~django.test.SimpleTestCase.assertNotContains` to test whether the
|
||||||
client's response contains a given HTML fragment. See the :ref:`assertions
|
client's response contains a given HTML fragment. See the :ref:`assertions
|
||||||
documentation <assertions>` for more.
|
documentation <assertions>` for more.
|
||||||
|
|
||||||
|
@ -1093,8 +1093,8 @@ wild, because they would confuse browsers too.
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
It's now possible to check whether a template was used within a block of
|
It's now possible to check whether a template was used within a block of
|
||||||
code with :meth:`~django.test.TestCase.assertTemplateUsed` and
|
code with :meth:`~django.test.SimpleTestCase.assertTemplateUsed` and
|
||||||
:meth:`~django.test.TestCase.assertTemplateNotUsed`. And they
|
:meth:`~django.test.SimpleTestCase.assertTemplateNotUsed`. And they
|
||||||
can be used as a context manager::
|
can be used as a context manager::
|
||||||
|
|
||||||
with self.assertTemplateUsed('index.html'):
|
with self.assertTemplateUsed('index.html'):
|
||||||
|
|
|
@ -271,9 +271,10 @@ The changes in transaction management may result in additional statements to
|
||||||
create, release or rollback savepoints. This is more likely to happen with
|
create, release or rollback savepoints. This is more likely to happen with
|
||||||
SQLite, since it didn't support savepoints until this release.
|
SQLite, since it didn't support savepoints until this release.
|
||||||
|
|
||||||
If tests using :meth:`~django.test.TestCase.assertNumQueries` fail because of
|
If tests using :meth:`~django.test.TransactionTestCase.assertNumQueries` fail
|
||||||
a higher number of queries than expected, check that the extra queries are
|
because of a higher number of queries than expected, check that the extra
|
||||||
related to savepoints, and adjust the expected number of queries accordingly.
|
queries are related to savepoints, and adjust the expected number of queries
|
||||||
|
accordingly.
|
||||||
|
|
||||||
Autocommit option for PostgreSQL
|
Autocommit option for PostgreSQL
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
@ -201,8 +201,8 @@ According to :pep:`3333`:
|
||||||
Specifically, :attr:`HttpResponse.content <django.http.HttpResponse.content>`
|
Specifically, :attr:`HttpResponse.content <django.http.HttpResponse.content>`
|
||||||
contains ``bytes``, which may become an issue if you compare it with a
|
contains ``bytes``, which may become an issue if you compare it with a
|
||||||
``str`` in your tests. The preferred solution is to rely on
|
``str`` in your tests. The preferred solution is to rely on
|
||||||
:meth:`~django.test.TestCase.assertContains` and
|
:meth:`~django.test.SimpleTestCase.assertContains` and
|
||||||
:meth:`~django.test.TestCase.assertNotContains`. These methods accept a
|
:meth:`~django.test.SimpleTestCase.assertNotContains`. These methods accept a
|
||||||
response and a unicode string as arguments.
|
response and a unicode string as arguments.
|
||||||
|
|
||||||
Coding guidelines
|
Coding guidelines
|
||||||
|
|
|
@ -21,17 +21,16 @@ module defines tests using a class-based approach.
|
||||||
|
|
||||||
.. admonition:: unittest2
|
.. admonition:: unittest2
|
||||||
|
|
||||||
Python 2.7 introduced some major changes to the unittest library,
|
Python 2.7 introduced some major changes to the ``unittest`` library,
|
||||||
adding some extremely useful features. To ensure that every Django
|
adding some extremely useful features. To ensure that every Django
|
||||||
project can benefit from these new features, Django ships with a
|
project can benefit from these new features, Django ships with a
|
||||||
copy of unittest2_, a copy of the Python 2.7 unittest library,
|
copy of unittest2_, a copy of Python 2.7's ``unittest``, backported for
|
||||||
backported for Python 2.6 compatibility.
|
Python 2.6 compatibility.
|
||||||
|
|
||||||
To access this library, Django provides the
|
To access this library, Django provides the
|
||||||
``django.utils.unittest`` module alias. If you are using Python
|
``django.utils.unittest`` module alias. If you are using Python
|
||||||
2.7, or you have installed unittest2 locally, Django will map the
|
2.7, or you have installed ``unittest2`` locally, Django will map the alias
|
||||||
alias to the installed version of the unittest library. Otherwise,
|
to it. Otherwise, Django will use its own bundled version of ``unittest2``.
|
||||||
Django will use its own bundled version of unittest2.
|
|
||||||
|
|
||||||
To use this alias, simply use::
|
To use this alias, simply use::
|
||||||
|
|
||||||
|
@ -41,8 +40,8 @@ module defines tests using a class-based approach.
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
If you want to continue to use the base unittest library, you can --
|
If you want to continue to use the legacy ``unittest`` library, you can --
|
||||||
you just won't get any of the nice new unittest2 features.
|
you just won't get any of the nice new ``unittest2`` features.
|
||||||
|
|
||||||
.. _unittest2: http://pypi.python.org/pypi/unittest2
|
.. _unittest2: http://pypi.python.org/pypi/unittest2
|
||||||
|
|
||||||
|
@ -858,24 +857,46 @@ SimpleTestCase
|
||||||
|
|
||||||
.. class:: SimpleTestCase()
|
.. class:: SimpleTestCase()
|
||||||
|
|
||||||
A very thin subclass of :class:`unittest.TestCase`, it extends it with some
|
A thin subclass of :class:`unittest.TestCase`, it extends it with some basic
|
||||||
basic functionality like:
|
functionality like:
|
||||||
|
|
||||||
* Saving and restoring the Python warning machinery state.
|
* Saving and restoring the Python warning machinery state.
|
||||||
* Checking that a callable :meth:`raises a certain exception <SimpleTestCase.assertRaisesMessage>`.
|
* Some useful assertions like:
|
||||||
* :meth:`Testing form field rendering <SimpleTestCase.assertFieldOutput>`.
|
|
||||||
* Testing server :ref:`HTML responses for the presence/lack of a given fragment <assertions>`.
|
* Checking that a callable :meth:`raises a certain exception
|
||||||
* The ability to run tests with :ref:`modified settings <overriding-settings>`
|
<SimpleTestCase.assertRaisesMessage>`.
|
||||||
|
* Testing form field :meth:`rendering and error treatment
|
||||||
|
<SimpleTestCase.assertFieldOutput>`.
|
||||||
|
* Testing :meth:`HTML responses for the presence/lack of a given fragment
|
||||||
|
<SimpleTestCase.assertContains>`.
|
||||||
|
* Verifying that a template :meth:`has/hasn't been used to generate a given
|
||||||
|
response content <SimpleTestCase.assertTemplateUsed>`.
|
||||||
|
* Verifying a HTTP :meth:`redirect <SimpleTestCase.assertRedirects>` is
|
||||||
|
performed by the app.
|
||||||
|
* Robustly testing two :meth:`HTML fragments <SimpleTestCase.assertHTMLEqual>`
|
||||||
|
for equality/inequality or :meth:`containment <SimpleTestCase.assertInHTML>`.
|
||||||
|
* Robustly testing two :meth:`XML fragments <SimpleTestCase.assertXMLEqual>`
|
||||||
|
for equality/inequality.
|
||||||
|
* Robustly testing two :meth:`JSON fragments <SimpleTestCase.assertJSONEqual>`
|
||||||
|
for equality.
|
||||||
|
|
||||||
|
* The ability to run tests with :ref:`modified settings <overriding-settings>`.
|
||||||
|
* Using the :attr:`~SimpleTestCase.client` :class:`~django.test.client.Client`.
|
||||||
|
* Custom test-time :attr:`URL maps <SimpleTestCase.urls>`.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.6
|
||||||
|
|
||||||
|
The latter two features were moved from ``TransactionTestCase`` to
|
||||||
|
``SimpleTestCase`` in Django 1.6.
|
||||||
|
|
||||||
If you need any of the other more complex and heavyweight Django-specific
|
If you need any of the other more complex and heavyweight Django-specific
|
||||||
features like:
|
features like:
|
||||||
|
|
||||||
* Using the :attr:`~TestCase.client` :class:`~django.test.client.Client`.
|
|
||||||
* Testing or using the ORM.
|
* Testing or using the ORM.
|
||||||
* Database :attr:`~TestCase.fixtures`.
|
* Database :attr:`~TransactionTestCase.fixtures`.
|
||||||
* Custom test-time :attr:`URL maps <TestCase.urls>`.
|
|
||||||
* Test :ref:`skipping based on database backend features <skipping-tests>`.
|
* Test :ref:`skipping based on database backend features <skipping-tests>`.
|
||||||
* The remaining specialized :ref:`assert* <assertions>` methods.
|
* The remaining specialized :meth:`assert*
|
||||||
|
<TransactionTestCase.assertQuerysetEqual>` methods.
|
||||||
|
|
||||||
then you should use :class:`~django.test.TransactionTestCase` or
|
then you should use :class:`~django.test.TransactionTestCase` or
|
||||||
:class:`~django.test.TestCase` instead.
|
:class:`~django.test.TestCase` instead.
|
||||||
|
@ -1137,9 +1158,9 @@ Test cases features
|
||||||
Default test client
|
Default test client
|
||||||
~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. attribute:: TestCase.client
|
.. attribute:: SimpleTestCase.client
|
||||||
|
|
||||||
Every test case in a ``django.test.TestCase`` instance has access to an
|
Every test case in a ``django.test.*TestCase`` instance has access to an
|
||||||
instance of a Django test client. This client can be accessed as
|
instance of a Django test client. This client can be accessed as
|
||||||
``self.client``. This client is recreated for each test, so you don't have to
|
``self.client``. This client is recreated for each test, so you don't have to
|
||||||
worry about state (such as cookies) carrying over from one test to another.
|
worry about state (such as cookies) carrying over from one test to another.
|
||||||
|
@ -1176,10 +1197,10 @@ This means, instead of instantiating a ``Client`` in each test::
|
||||||
Customizing the test client
|
Customizing the test client
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. attribute:: TestCase.client_class
|
.. attribute:: SimpleTestCase.client_class
|
||||||
|
|
||||||
If you want to use a different ``Client`` class (for example, a subclass
|
If you want to use a different ``Client`` class (for example, a subclass
|
||||||
with customized behavior), use the :attr:`~TestCase.client_class` class
|
with customized behavior), use the :attr:`~SimpleTestCase.client_class` class
|
||||||
attribute::
|
attribute::
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
@ -1200,11 +1221,12 @@ attribute::
|
||||||
Fixture loading
|
Fixture loading
|
||||||
~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. attribute:: TestCase.fixtures
|
.. attribute:: TransactionTestCase.fixtures
|
||||||
|
|
||||||
A test case for a database-backed Web site isn't much use if there isn't any
|
A test case for a database-backed Web site isn't much use if there isn't any
|
||||||
data in the database. To make it easy to put test data into the database,
|
data in the database. To make it easy to put test data into the database,
|
||||||
Django's custom ``TestCase`` class provides a way of loading **fixtures**.
|
Django's custom ``TransactionTestCase`` class provides a way of loading
|
||||||
|
**fixtures**.
|
||||||
|
|
||||||
A fixture is a collection of data that Django knows how to import into a
|
A fixture is a collection of data that Django knows how to import into a
|
||||||
database. For example, if your site has user accounts, you might set up a
|
database. For example, if your site has user accounts, you might set up a
|
||||||
|
@ -1273,7 +1295,7 @@ or by the order of test execution.
|
||||||
URLconf configuration
|
URLconf configuration
|
||||||
~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. attribute:: TestCase.urls
|
.. attribute:: SimpleTestCase.urls
|
||||||
|
|
||||||
If your application provides views, you may want to include tests that use the
|
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
|
test client to exercise those views. However, an end user is free to deploy the
|
||||||
|
@ -1282,9 +1304,9 @@ tests can't rely upon the fact that your views will be available at a
|
||||||
particular URL.
|
particular URL.
|
||||||
|
|
||||||
In order to provide a reliable URL space for your test,
|
In order to provide a reliable URL space for your test,
|
||||||
``django.test.TestCase`` provides the ability to customize the URLconf
|
``django.test.*TestCase`` classes provide the ability to customize the URLconf
|
||||||
configuration for the duration of the execution of a test suite. If your
|
configuration for the duration of the execution of a test suite. If your
|
||||||
``TestCase`` instance defines an ``urls`` attribute, the ``TestCase`` will use
|
``*TestCase`` instance defines an ``urls`` attribute, the ``*TestCase`` will use
|
||||||
the value of that attribute as the :setting:`ROOT_URLCONF` for the duration
|
the value of that attribute as the :setting:`ROOT_URLCONF` for the duration
|
||||||
of that test.
|
of that test.
|
||||||
|
|
||||||
|
@ -1307,7 +1329,7 @@ URLconf for the duration of the test case.
|
||||||
Multi-database support
|
Multi-database support
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. attribute:: TestCase.multi_db
|
.. attribute:: TransactionTestCase.multi_db
|
||||||
|
|
||||||
Django sets up a test database corresponding to every database that is
|
Django sets up a test database corresponding to every database that is
|
||||||
defined in the :setting:`DATABASES` definition in your settings
|
defined in the :setting:`DATABASES` definition in your settings
|
||||||
|
@ -1340,12 +1362,12 @@ This test case will flush *all* the test databases before running
|
||||||
Overriding settings
|
Overriding settings
|
||||||
~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
.. method:: TestCase.settings
|
.. method:: SimpleTestCase.settings
|
||||||
|
|
||||||
For testing purposes it's often useful to change a setting temporarily and
|
For testing purposes it's often useful to change a setting temporarily and
|
||||||
revert to the original value after running the testing code. For this use case
|
revert to the original value after running the testing code. For this use case
|
||||||
Django provides a standard Python context manager (see :pep:`343`)
|
Django provides a standard Python context manager (see :pep:`343`)
|
||||||
:meth:`~django.test.TestCase.settings`, which can be used like this::
|
:meth:`~django.test.SimpleTestCase.settings`, which can be used like this::
|
||||||
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
|
||||||
|
@ -1435,8 +1457,8 @@ MEDIA_ROOT, DEFAULT_FILE_STORAGE Default file storage
|
||||||
Emptying the test outbox
|
Emptying the test outbox
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
If you use Django's custom ``TestCase`` class, the test runner will clear the
|
If you use any of Django's custom ``TestCase`` classes, the test runner will
|
||||||
contents of the test email outbox at the start of each test case.
|
clear thecontents of the test email outbox at the start of each test case.
|
||||||
|
|
||||||
For more detail on email services during tests, see `Email services`_ below.
|
For more detail on email services during tests, see `Email services`_ below.
|
||||||
|
|
||||||
|
@ -1486,31 +1508,7 @@ your test suite.
|
||||||
|
|
||||||
self.assertFieldOutput(EmailField, {'a@a.com': 'a@a.com'}, {'aaa': [u'Enter a valid email address.']})
|
self.assertFieldOutput(EmailField, {'a@a.com': 'a@a.com'}, {'aaa': [u'Enter a valid email address.']})
|
||||||
|
|
||||||
|
.. method:: SimpleTestCase.assertFormError(response, form, field, errors, msg_prefix='')
|
||||||
.. method:: TestCase.assertContains(response, text, count=None, status_code=200, msg_prefix='', html=False)
|
|
||||||
|
|
||||||
Asserts that a ``Response`` instance produced the given ``status_code`` and
|
|
||||||
that ``text`` appears in the content of the response. If ``count`` is
|
|
||||||
provided, ``text`` must occur exactly ``count`` times in the response.
|
|
||||||
|
|
||||||
Set ``html`` to ``True`` to handle ``text`` as HTML. The comparison with
|
|
||||||
the response content will be based on HTML semantics instead of
|
|
||||||
character-by-character equality. Whitespace is ignored in most cases,
|
|
||||||
attribute ordering is not significant. See
|
|
||||||
:meth:`~SimpleTestCase.assertHTMLEqual` for more details.
|
|
||||||
|
|
||||||
.. method:: TestCase.assertNotContains(response, text, status_code=200, msg_prefix='', html=False)
|
|
||||||
|
|
||||||
Asserts that a ``Response`` instance produced the given ``status_code`` and
|
|
||||||
that ``text`` does not appears in the content of the response.
|
|
||||||
|
|
||||||
Set ``html`` to ``True`` to handle ``text`` as HTML. The comparison with
|
|
||||||
the response content will be based on HTML semantics instead of
|
|
||||||
character-by-character equality. Whitespace is ignored in most cases,
|
|
||||||
attribute ordering is not significant. See
|
|
||||||
:meth:`~SimpleTestCase.assertHTMLEqual` for more details.
|
|
||||||
|
|
||||||
.. method:: TestCase.assertFormError(response, form, field, errors, msg_prefix='')
|
|
||||||
|
|
||||||
Asserts that a field on a form raises the provided list of errors when
|
Asserts that a field on a form raises the provided list of errors when
|
||||||
rendered on the form.
|
rendered on the form.
|
||||||
|
@ -1525,7 +1523,30 @@ your test suite.
|
||||||
``errors`` is an error string, or a list of error strings, that are
|
``errors`` is an error string, or a list of error strings, that are
|
||||||
expected as a result of form validation.
|
expected as a result of form validation.
|
||||||
|
|
||||||
.. method:: TestCase.assertTemplateUsed(response, template_name, msg_prefix='')
|
.. method:: SimpleTestCase.assertContains(response, text, count=None, status_code=200, msg_prefix='', html=False)
|
||||||
|
|
||||||
|
Asserts that a ``Response`` instance produced the given ``status_code`` and
|
||||||
|
that ``text`` appears in the content of the response. If ``count`` is
|
||||||
|
provided, ``text`` must occur exactly ``count`` times in the response.
|
||||||
|
|
||||||
|
Set ``html`` to ``True`` to handle ``text`` as HTML. The comparison with
|
||||||
|
the response content will be based on HTML semantics instead of
|
||||||
|
character-by-character equality. Whitespace is ignored in most cases,
|
||||||
|
attribute ordering is not significant. See
|
||||||
|
:meth:`~SimpleTestCase.assertHTMLEqual` for more details.
|
||||||
|
|
||||||
|
.. method:: SimpleTestCase.assertNotContains(response, text, status_code=200, msg_prefix='', html=False)
|
||||||
|
|
||||||
|
Asserts that a ``Response`` instance produced the given ``status_code`` and
|
||||||
|
that ``text`` does not appears in the content of the response.
|
||||||
|
|
||||||
|
Set ``html`` to ``True`` to handle ``text`` as HTML. The comparison with
|
||||||
|
the response content will be based on HTML semantics instead of
|
||||||
|
character-by-character equality. Whitespace is ignored in most cases,
|
||||||
|
attribute ordering is not significant. See
|
||||||
|
:meth:`~SimpleTestCase.assertHTMLEqual` for more details.
|
||||||
|
|
||||||
|
.. method:: SimpleTestCase.assertTemplateUsed(response, template_name, msg_prefix='')
|
||||||
|
|
||||||
Asserts that the template with the given name was used in rendering the
|
Asserts that the template with the given name was used in rendering the
|
||||||
response.
|
response.
|
||||||
|
@ -1539,15 +1560,15 @@ your test suite.
|
||||||
with self.assertTemplateUsed(template_name='index.html'):
|
with self.assertTemplateUsed(template_name='index.html'):
|
||||||
render_to_string('index.html')
|
render_to_string('index.html')
|
||||||
|
|
||||||
.. method:: TestCase.assertTemplateNotUsed(response, template_name, msg_prefix='')
|
.. method:: SimpleTestCase.assertTemplateNotUsed(response, template_name, msg_prefix='')
|
||||||
|
|
||||||
Asserts that the template with the given name was *not* used in rendering
|
Asserts that the template with the given name was *not* used in rendering
|
||||||
the response.
|
the response.
|
||||||
|
|
||||||
You can use this as a context manager in the same way as
|
You can use this as a context manager in the same way as
|
||||||
:meth:`~TestCase.assertTemplateUsed`.
|
:meth:`~SimpleTestCase.assertTemplateUsed`.
|
||||||
|
|
||||||
.. method:: TestCase.assertRedirects(response, expected_url, status_code=302, target_status_code=200, msg_prefix='')
|
.. method:: SimpleTestCase.assertRedirects(response, expected_url, status_code=302, target_status_code=200, msg_prefix='')
|
||||||
|
|
||||||
Asserts that the response return a ``status_code`` redirect status, it
|
Asserts that the response return a ``status_code`` redirect status, it
|
||||||
redirected to ``expected_url`` (including any GET data), and the final
|
redirected to ``expected_url`` (including any GET data), and the final
|
||||||
|
@ -1557,44 +1578,6 @@ your test suite.
|
||||||
``target_status_code`` will be the url and status code for the final
|
``target_status_code`` will be the url and status code for the final
|
||||||
point of the redirect chain.
|
point of the redirect chain.
|
||||||
|
|
||||||
.. method:: TestCase.assertQuerysetEqual(qs, values, transform=repr, ordered=True)
|
|
||||||
|
|
||||||
Asserts that a queryset ``qs`` returns a particular list of values ``values``.
|
|
||||||
|
|
||||||
The comparison of the contents of ``qs`` and ``values`` is performed using
|
|
||||||
the function ``transform``; by default, this means that the ``repr()`` of
|
|
||||||
each value is compared. Any other callable can be used if ``repr()`` doesn't
|
|
||||||
provide a unique or helpful comparison.
|
|
||||||
|
|
||||||
By default, the comparison is also ordering dependent. If ``qs`` doesn't
|
|
||||||
provide an implicit ordering, you can set the ``ordered`` parameter to
|
|
||||||
``False``, which turns the comparison into a Python set comparison.
|
|
||||||
|
|
||||||
.. versionchanged:: 1.6
|
|
||||||
|
|
||||||
The method now checks for undefined order and raises ``ValueError``
|
|
||||||
if undefined order is spotted. The ordering is seen as undefined if
|
|
||||||
the given ``qs`` isn't ordered and the comparison is against more
|
|
||||||
than one ordered values.
|
|
||||||
|
|
||||||
.. method:: TestCase.assertNumQueries(num, func, *args, **kwargs)
|
|
||||||
|
|
||||||
Asserts that when ``func`` is called with ``*args`` and ``**kwargs`` that
|
|
||||||
``num`` database queries are executed.
|
|
||||||
|
|
||||||
If a ``"using"`` key is present in ``kwargs`` it is used as the database
|
|
||||||
alias for which to check the number of queries. If you wish to call a
|
|
||||||
function with a ``using`` parameter you can do it by wrapping the call with
|
|
||||||
a ``lambda`` to add an extra parameter::
|
|
||||||
|
|
||||||
self.assertNumQueries(7, lambda: my_function(using=7))
|
|
||||||
|
|
||||||
You can also use this as a context manager::
|
|
||||||
|
|
||||||
with self.assertNumQueries(2):
|
|
||||||
Person.objects.create(name="Aaron")
|
|
||||||
Person.objects.create(name="Daniel")
|
|
||||||
|
|
||||||
.. method:: SimpleTestCase.assertHTMLEqual(html1, html2, msg=None)
|
.. method:: SimpleTestCase.assertHTMLEqual(html1, html2, msg=None)
|
||||||
|
|
||||||
Asserts that the strings ``html1`` and ``html2`` are equal. The comparison
|
Asserts that the strings ``html1`` and ``html2`` are equal. The comparison
|
||||||
|
@ -1624,6 +1607,8 @@ your test suite.
|
||||||
``html1`` and ``html2`` must be valid HTML. An ``AssertionError`` will be
|
``html1`` and ``html2`` must be valid HTML. An ``AssertionError`` will be
|
||||||
raised if one of them cannot be parsed.
|
raised if one of them cannot be parsed.
|
||||||
|
|
||||||
|
Output in case of error can be customized with the ``msg`` argument.
|
||||||
|
|
||||||
.. method:: SimpleTestCase.assertHTMLNotEqual(html1, html2, msg=None)
|
.. method:: SimpleTestCase.assertHTMLNotEqual(html1, html2, msg=None)
|
||||||
|
|
||||||
Asserts that the strings ``html1`` and ``html2`` are *not* equal. The
|
Asserts that the strings ``html1`` and ``html2`` are *not* equal. The
|
||||||
|
@ -1633,6 +1618,8 @@ your test suite.
|
||||||
``html1`` and ``html2`` must be valid HTML. An ``AssertionError`` will be
|
``html1`` and ``html2`` must be valid HTML. An ``AssertionError`` will be
|
||||||
raised if one of them cannot be parsed.
|
raised if one of them cannot be parsed.
|
||||||
|
|
||||||
|
Output in case of error can be customized with the ``msg`` argument.
|
||||||
|
|
||||||
.. method:: SimpleTestCase.assertXMLEqual(xml1, xml2, msg=None)
|
.. method:: SimpleTestCase.assertXMLEqual(xml1, xml2, msg=None)
|
||||||
|
|
||||||
.. versionadded:: 1.5
|
.. versionadded:: 1.5
|
||||||
|
@ -1644,6 +1631,8 @@ your test suite.
|
||||||
syntax differences. When unvalid XML is passed in any parameter, an
|
syntax differences. When unvalid XML is passed in any parameter, an
|
||||||
``AssertionError`` is always raised, even if both string are identical.
|
``AssertionError`` is always raised, even if both string are identical.
|
||||||
|
|
||||||
|
Output in case of error can be customized with the ``msg`` argument.
|
||||||
|
|
||||||
.. method:: SimpleTestCase.assertXMLNotEqual(xml1, xml2, msg=None)
|
.. method:: SimpleTestCase.assertXMLNotEqual(xml1, xml2, msg=None)
|
||||||
|
|
||||||
.. versionadded:: 1.5
|
.. versionadded:: 1.5
|
||||||
|
@ -1652,6 +1641,68 @@ your test suite.
|
||||||
comparison is based on XML semantics. See
|
comparison is based on XML semantics. See
|
||||||
:meth:`~SimpleTestCase.assertXMLEqual` for details.
|
:meth:`~SimpleTestCase.assertXMLEqual` for details.
|
||||||
|
|
||||||
|
Output in case of error can be customized with the ``msg`` argument.
|
||||||
|
|
||||||
|
.. method:: SimpleTestCase.assertInHTML(needle, haystack, count=None, msg_prefix='')
|
||||||
|
|
||||||
|
.. versionadded:: 1.5
|
||||||
|
|
||||||
|
Asserts that the HTML fragment ``needle`` is contained in the ``haystack`` one.
|
||||||
|
|
||||||
|
If the ``count`` integer argument is specified, then additionally the number
|
||||||
|
of ``needle`` occurrences will be strictly verified.
|
||||||
|
|
||||||
|
Whitespace in most cases is ignored, and attribute ordering is not
|
||||||
|
significant. The passed-in arguments must be valid HTML.
|
||||||
|
|
||||||
|
.. method:: SimpleTestCase.assertJSONEqual(raw, expected_data, msg=None)
|
||||||
|
|
||||||
|
.. versionadded:: 1.5
|
||||||
|
|
||||||
|
Asserts that the JSON fragments ``raw`` and ``expected_data`` are equal.
|
||||||
|
Usual JSON non-significant whitespace rules apply as the heavyweight is
|
||||||
|
delegated to the :mod:`json` library.
|
||||||
|
|
||||||
|
Output in case of error can be customized with the ``msg`` argument.
|
||||||
|
|
||||||
|
.. method:: TransactionTestCase.assertQuerysetEqual(qs, values, transform=repr, ordered=True)
|
||||||
|
|
||||||
|
Asserts that a queryset ``qs`` returns a particular list of values ``values``.
|
||||||
|
|
||||||
|
The comparison of the contents of ``qs`` and ``values`` is performed using
|
||||||
|
the function ``transform``; by default, this means that the ``repr()`` of
|
||||||
|
each value is compared. Any other callable can be used if ``repr()`` doesn't
|
||||||
|
provide a unique or helpful comparison.
|
||||||
|
|
||||||
|
By default, the comparison is also ordering dependent. If ``qs`` doesn't
|
||||||
|
provide an implicit ordering, you can set the ``ordered`` parameter to
|
||||||
|
``False``, which turns the comparison into a Python set comparison.
|
||||||
|
|
||||||
|
.. versionchanged:: 1.6
|
||||||
|
|
||||||
|
The method now checks for undefined order and raises ``ValueError``
|
||||||
|
if undefined order is spotted. The ordering is seen as undefined if
|
||||||
|
the given ``qs`` isn't ordered and the comparison is against more
|
||||||
|
than one ordered values.
|
||||||
|
|
||||||
|
.. method:: TransactionTestCase.assertNumQueries(num, func, *args, **kwargs)
|
||||||
|
|
||||||
|
Asserts that when ``func`` is called with ``*args`` and ``**kwargs`` that
|
||||||
|
``num`` database queries are executed.
|
||||||
|
|
||||||
|
If a ``"using"`` key is present in ``kwargs`` it is used as the database
|
||||||
|
alias for which to check the number of queries. If you wish to call a
|
||||||
|
function with a ``using`` parameter you can do it by wrapping the call with
|
||||||
|
a ``lambda`` to add an extra parameter::
|
||||||
|
|
||||||
|
self.assertNumQueries(7, lambda: my_function(using=7))
|
||||||
|
|
||||||
|
You can also use this as a context manager::
|
||||||
|
|
||||||
|
with self.assertNumQueries(2):
|
||||||
|
Person.objects.create(name="Aaron")
|
||||||
|
Person.objects.create(name="Daniel")
|
||||||
|
|
||||||
.. _topics-testing-email:
|
.. _topics-testing-email:
|
||||||
|
|
||||||
Email services
|
Email services
|
||||||
|
@ -1701,7 +1752,7 @@ and contents::
|
||||||
self.assertEqual(mail.outbox[0].subject, 'Subject here')
|
self.assertEqual(mail.outbox[0].subject, 'Subject here')
|
||||||
|
|
||||||
As noted :ref:`previously <emptying-test-outbox>`, the test outbox is emptied
|
As noted :ref:`previously <emptying-test-outbox>`, the test outbox is emptied
|
||||||
at the start of every test in a Django ``TestCase``. To empty the outbox
|
at the start of every test in a Django ``*TestCase``. To empty the outbox
|
||||||
manually, assign the empty list to ``mail.outbox``::
|
manually, assign the empty list to ``mail.outbox``::
|
||||||
|
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
|
|
Loading…
Reference in New Issue