Commit 326949e4 authored by Ramiro Morales's avatar Ramiro Morales
Browse files

Fixed #14503 -- Unified multiple implementations of test cases assert* methods...

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
parent a539d434
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -3,5 +3,6 @@ Django Unit Test and Doctest framework.
"""

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
+37 −22
Original line number Diff line number Diff line
@@ -21,7 +21,7 @@ from django.utils import simplejson, unittest as ut2
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 @@ class _AssertNumQueriesContext(object):
            )
        )

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 @@ class TransactionTestCase(ut2.TestCase):
            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
+1 −1
Original line number Diff line number Diff line
@@ -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.

+57 −13
Original line number Diff line number Diff line
@@ -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.
+0 −6
Original line number Diff line number Diff line
@@ -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):
Loading