Permalink
Browse files

Fixed #14503 -- Unified multiple implementations of test cases assert…

…* methods that verify a given exception is raised by a callable throughout the Django test suite.

Replaced them with a new assertRaisesMessage method of a new SimpleTestCase, a lightweight subclass of unittest.TestCase. Both are also available for usage in user tests.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@16610 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
1 parent a539d43 commit 326949e444bcdde966d2421e2fb8bd8c87c65941 @ramiro ramiro committed Aug 13, 2011
@@ -3,5 +3,6 @@
"""
from django.test.client import Client, RequestFactory
-from django.test.testcases import TestCase, TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature
+from django.test.testcases import (TestCase, TransactionTestCase,
+ SimpleTestCase, skipIfDBFeature, skipUnlessDBFeature)
from django.test.utils import Approximate
@@ -21,7 +21,7 @@
from django.utils.encoding import smart_str
__all__ = ('DocTestRunner', 'OutputChecker', 'TestCase', 'TransactionTestCase',
- 'skipIfDBFeature', 'skipUnlessDBFeature')
+ 'SimpleTestCase', 'skipIfDBFeature', 'skipUnlessDBFeature')
normalize_long_ints = lambda s: re.sub(r'(?<![\w])(\d+)L(?![\w])', '\\1', s)
normalize_decimals = lambda s: re.sub(r"Decimal\('(\d+(\.\d*)?)'\)", lambda m: "Decimal(\"%s\")" % m.groups()[0], s)
@@ -235,8 +235,43 @@ def __exit__(self, exc_type, exc_value, traceback):
)
)
+class SimpleTestCase(ut2.TestCase):
-class TransactionTestCase(ut2.TestCase):
+ def save_warnings_state(self):
+ """
+ Saves the state of the warnings module
+ """
+ self._warnings_state = get_warnings_state()
+
+ def restore_warnings_state(self):
+ """
+ Restores the sate of the warnings module to the state
+ saved by save_warnings_state()
+ """
+ restore_warnings_state(self._warnings_state)
+
+ def settings(self, **kwargs):
+ """
+ A context manager that temporarily sets a setting and reverts
+ back to the original value when exiting the context.
+ """
+ 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 passe 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 self.assertRaisesRegexp(expected_exception,
+ re.escape(expected_message), callable_obj, *args, **kwargs)
+
+class TransactionTestCase(SimpleTestCase):
# The class we'll use for the test client self.client.
# Can be overridden in derived classes.
client_class = Client
@@ -332,26 +367,6 @@ def _urlconf_teardown(self):
settings.ROOT_URLCONF = self._old_root_urlconf
clear_url_caches()
- def save_warnings_state(self):
- """
- Saves the state of the warnings module
- """
- self._warnings_state = get_warnings_state()
-
- def restore_warnings_state(self):
- """
- Restores the sate of the warnings module to the state
- saved by save_warnings_state()
- """
- restore_warnings_state(self._warnings_state)
-
- def settings(self, **kwargs):
- """
- A context manager that temporarily sets a setting and reverts
- back to the original value when exiting the context.
- """
- return override_settings(**kwargs)
-
def assertRedirects(self, response, expected_url, status_code=302,
target_status_code=200, host=None, msg_prefix=''):
"""Asserts that a response redirected to a specific URL, and that the
@@ -480,7 +480,7 @@ setting_changed
.. data:: django.test.signals.setting_changed
:module:
-Sent when some :ref:`settings are overridden <overriding-setting>` with the
+Sent when some :ref:`settings are overridden <overriding-settings>` with the
:meth:`django.test.TestCase.setting` context manager or the
:func:`django.test.utils.override_settings` decorator/context manager.
@@ -715,7 +715,7 @@ arguments at time of construction:
The headers sent via ``**extra`` should follow CGI_ specification.
For example, emulating a different "Host" header as sent in the
HTTP request from the browser to the server should be passed
- as ``HTTP_HOST``.
+ as ``HTTP_HOST``.
.. _CGI: http://www.w3.org/CGI/
@@ -1101,7 +1101,7 @@ TestCase
.. currentmodule:: django.test
Normal Python unit test classes extend a base class of ``unittest.TestCase``.
-Django provides an extension of this base class:
+Django provides a few extensions of this base class:
.. class:: TestCase()
@@ -1123,6 +1123,8 @@ additions, including:
* Django-specific assertions for testing for things
like redirection and form errors.
+``TestCase`` inherits from :class:`~django.test.TransactionTestCase`.
+
.. class:: TransactionTestCase()
Django ``TestCase`` classes make use of database transaction facilities, if
@@ -1153,6 +1155,7 @@ When running on a database that does not support rollback (e.g. MySQL with the
MyISAM storage engine), ``TestCase`` falls back to initializing the database
by truncating tables and reloading initial data.
+``TransactionTestCase`` inherits from :class:`~django.test.SimpleTestCase`.
.. note::
The ``TestCase`` use of rollback to un-do the effects of the test code
@@ -1166,6 +1169,31 @@ by truncating tables and reloading initial data.
A better long-term fix, that allows the test to take advantage of the
speed benefit of ``TestCase``, is to fix the underlying test problem.
+.. class:: SimpleTestCase()
+
+.. versionadded:: 1.4
+
+A very thin subclass of :class:`unittest.TestCase`, it extends it with some
+basic functionality like:
+
+ * Saving and restoring the Python warning machinery state.
+ * Checking that a callable :meth:`raises a certain exeception <TestCase.assertRaisesMessage>`.
+
+If you need any of the other more complex and heavyweight Django-specific
+features like:
+
+ * The ability to run tests with :ref:`modified settings <overriding-settings>`
+ * Using the :attr:`~TestCase.client` :class:`~django.test.client.Client`.
+ * Testing or using the ORM.
+ * Database :attr:`~TestCase.fixtures`.
+ * Custom test-time :attr:`URL maps <TestCase.urls>`.
+ * Test :ref:`skipping based on database backend features <skipping-tests>`.
+ * Our specialized :ref:`assert* <assertions>` metods.
+
+then you should use :class:`~django.test.TransactionTestCase` or
+:class:`~django.test.TestCase` instead.
+
+``SimpleTestCase`` inherits from :class:`django.utils.unittest.TestCase`.
Default test client
~~~~~~~~~~~~~~~~~~~
@@ -1370,7 +1398,7 @@ For example::
This test case will flush *all* the test databases before running
``testIndexPageView``.
-.. _overriding-setting:
+.. _overriding-settings:
Overriding settings
~~~~~~~~~~~~~~~~~~~
@@ -1402,7 +1430,9 @@ this use case Django provides a standard `Python context manager`_
This example will override the :setting:`LOGIN_URL` setting for the code
in the ``with`` block and reset its value to the previous state afterwards.
-.. function:: utils.override_settings
+.. currentmodule:: django.test.utils
+
+.. function:: override_settings
In case you want to override a setting for just one test method or even the
whole TestCase class, Django provides the
@@ -1463,9 +1493,13 @@ contents of the test email outbox at the start of each test case.
For more detail on email services during tests, see `Email services`_.
+.. _assertions:
+
Assertions
~~~~~~~~~~
+.. currentmodule:: django.test
+
.. versionchanged:: 1.2
Addded ``msg_prefix`` argument.
@@ -1474,11 +1508,19 @@ such as ``assertTrue`` and ``assertEqual``, Django's custom ``TestCase`` class
provides a number of custom assertion methods that are useful for testing Web
applications:
-The failure messages given by the assertion methods can be customized
-with the ``msg_prefix`` argument. This string will be prefixed to any
-failure message generated by the assertion. This allows you to provide
-additional details that may help you to identify the location and
-cause of an failure in your test suite.
+The failure messages given by most of these assertion methods can be customized
+with the ``msg_prefix`` argument. This string will be prefixed to any failure
+message generated by the assertion. This allows you to provide additional
+details that may help you to identify the location and cause of an failure in
+your test suite.
+
+.. method:: TestCase.assertRaisesMessage(expected_exception, expected_message, callable_obj=None, *args, **kwargs)
+
+ Asserts that execution of callable ``callable_obj`` raised the
+ ``expected_exception`` exception and that such exception has an
+ ``expected_message`` representation. Any other outcome is reported as a
+ failure. Similar to unittest's ``assertRaisesRegexp`` with the difference
+ that ``expected_message`` isn't a regular expression.
.. method:: TestCase.assertContains(response, text, count=None, status_code=200, msg_prefix='')
@@ -1626,9 +1668,13 @@ manually, assign the empty list to ``mail.outbox``::
# Empty the test outbox
mail.outbox = []
+.. _skipping-tests:
+
Skipping tests
--------------
+.. currentmodule:: django.test
+
.. versionadded:: 1.3
The unittest library provides the ``@skipIf`` and ``@skipUnless``
@@ -1651,8 +1697,7 @@ features class. See :class:`~django.db.backends.BaseDatabaseFeatures`
class for a full list of database features that can be used as a basis
for skipping tests.
-skipIfDBFeature
-~~~~~~~~~~~~~~~
+.. function:: skipIfDBFeature(feature_name_string)
Skip the decorated test if the named database feature is supported.
@@ -1665,8 +1710,7 @@ it would under MySQL with MyISAM tables)::
def test_transaction_behavior(self):
# ... conditional test code
-skipUnlessDBFeature
-~~~~~~~~~~~~~~~~~~~
+.. function:: skipUnlessDBFeature(feature_name_string)
Skip the decorated test if the named database feature is *not*
supported.
@@ -19,12 +19,6 @@ class InvalidFields(admin.ModelAdmin):
fields = ['spam']
class ValidationTestCase(TestCase):
- def assertRaisesMessage(self, exc, msg, func, *args, **kwargs):
- try:
- func(*args, **kwargs)
- except Exception, e:
- self.assertEqual(msg, str(e))
- self.assertTrue(isinstance(e, exc), "Expected %s, got %s" % (exc, type(e)))
def test_readonly_and_editable(self):
class SongAdmin(admin.ModelAdmin):
@@ -9,13 +9,6 @@ def pks(objects):
class CustomColumnRegression(TestCase):
- def assertRaisesMessage(self, exc, msg, func, *args, **kwargs):
- try:
- func(*args, **kwargs)
- except Exception, e:
- self.assertEqual(msg, str(e))
- self.assertTrue(isinstance(e, exc), "Expected %s, got %s" % (exc, type(e)))
-
def setUp(self):
self.a1 = Author.objects.create(first_name='John', last_name='Smith')
self.a2 = Author.objects.create(first_name='Peter', last_name='Jones')
@@ -22,6 +22,7 @@
from django.core.files.images import get_image_dimensions
from django.core.files.storage import FileSystemStorage, get_storage_class
from django.core.files.uploadedfile import UploadedFile
+from django.test import SimpleTestCase
from django.utils import unittest
# Try to import PIL in either of the two ways it can end up installed.
@@ -36,14 +37,7 @@
Image = None
-class GetStorageClassTests(unittest.TestCase):
- def assertRaisesErrorWithMessage(self, error, message, callable,
- *args, **kwargs):
- self.assertRaises(error, callable, *args, **kwargs)
- try:
- callable(*args, **kwargs)
- except error, e:
- self.assertEqual(message, str(e))
+class GetStorageClassTests(SimpleTestCase):
def test_get_filesystem_storage(self):
"""
@@ -57,7 +51,7 @@ def test_get_invalid_storage_module(self):
"""
get_storage_class raises an error if the requested import don't exist.
"""
- self.assertRaisesErrorWithMessage(
+ self.assertRaisesMessage(
ImproperlyConfigured,
"NonExistingStorage isn't a storage module.",
get_storage_class,
@@ -67,7 +61,7 @@ def test_get_nonexisting_storage_class(self):
"""
get_storage_class raises an error if the requested class don't exist.
"""
- self.assertRaisesErrorWithMessage(
+ self.assertRaisesMessage(
ImproperlyConfigured,
'Storage module "django.core.files.storage" does not define a '\
'"NonExistingStorage" class.',
@@ -393,12 +393,6 @@ def test_loaddata_raises_error_when_fixture_has_invalid_foreign_key(self):
class NaturalKeyFixtureTests(TestCase):
- def assertRaisesMessage(self, exc, msg, func, *args, **kwargs):
- try:
- func(*args, **kwargs)
- except Exception, e:
- self.assertEqual(msg, str(e))
- self.assertTrue(isinstance(e, exc), "Expected %s, got %s" % (exc, type(e)))
def test_nk_deserialize(self):
"""
Oops, something went wrong.

0 comments on commit 326949e

Please sign in to comment.