diff --git a/django/test/testcases.py b/django/test/testcases.py index 34edd15db7..4a9b0c3ee8 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -8,11 +8,14 @@ from xml.dom.minidom import parseString, Node from django.conf import settings from django.core import mail +from django.core.exceptions import ValidationError from django.core.management import call_command from django.core.signals import request_started from django.core.urlresolvers import clear_url_caches +from django.core.validators import EMPTY_VALUES from django.db import (transaction, connection, connections, DEFAULT_DB_ALIAS, reset_queries) +from django.forms.fields import CharField from django.http import QueryDict from django.test import _doctest as doctest from django.test.client import Client @@ -271,6 +274,53 @@ class SimpleTestCase(ut2.TestCase): return self.assertRaisesRegexp(expected_exception, re.escape(expected_message), callable_obj, *args, **kwargs) + def assertFieldOutput(self, fieldclass, valid, invalid, field_args=None, + field_kwargs=None, empty_value=u''): + """ + 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 = [u'This field is required.'] + for e in 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)) + class TransactionTestCase(SimpleTestCase): # The class we'll use for the test client self.client. # Can be overridden in derived classes. @@ -356,8 +406,8 @@ class TransactionTestCase(SimpleTestCase): # be created with the wrong time). # To make sure this doesn't happen, get a clean connection at the # start of every test. - for connection in connections.all(): - connection.close() + for conn in connections.all(): + conn.close() def _fixture_teardown(self): pass @@ -552,9 +602,9 @@ class TransactionTestCase(SimpleTestCase): def assertNumQueries(self, num, func=None, *args, **kwargs): using = kwargs.pop("using", DEFAULT_DB_ALIAS) - connection = connections[using] + conn = connections[using] - context = _AssertNumQueriesContext(self, num, connection) + context = _AssertNumQueriesContext(self, num, conn) if func is None: return context diff --git a/docs/topics/testing.txt b/docs/topics/testing.txt index 7bef4cb52f..eef5b138b1 100644 --- a/docs/topics/testing.txt +++ b/docs/topics/testing.txt @@ -1178,6 +1178,7 @@ basic functionality like: * Saving and restoring the Python warning machinery state. * Checking that a callable :meth:`raises a certain exeception `. + * :meth:`Testing form field rendering `. If you need any of the other more complex and heavyweight Django-specific features like: @@ -1523,6 +1524,26 @@ your test suite. failure. Similar to unittest's ``assertRaisesRegexp`` with the difference that ``expected_message`` isn't a regular expression. +.. method:: assertFieldOutput(self, fieldclass, valid, invalid, field_args=None, field_kwargs=None, empty_value=u'') + + Asserts that a form field behaves correctly with various inputs. + + :param fieldclass: the class of the field to be tested. + :param valid: a dictionary mapping valid inputs to their expected cleaned + values. + :param invalid: a dictionary mapping invalid inputs to one or more raised + error messages. + :param field_args: the args passed to instantiate the field. + :param field_kwargs: the kwargs passed to instantiate the field. + :param empty_value: the expected clean output for inputs in ``EMPTY_VALUES``. + + For example, the following code tests that an ``EmailField`` accepts + "a@a.com" as a valid email address, but rejects "aaa" with a reasonable + error message:: + + self.assertFieldOutput(EmailField, {'a@a.com': 'a@a.com'}, {'aaa': [u'Enter a valid e-mail address.']}) + + .. method:: TestCase.assertContains(response, text, count=None, status_code=200, msg_prefix='') Asserts that a ``Response`` instance produced the given ``status_code`` and diff --git a/tests/regressiontests/forms/localflavor/__init__.py b/tests/regressiontests/forms/localflavor/__init__.py index b324e07810..792d600548 100644 --- a/tests/regressiontests/forms/localflavor/__init__.py +++ b/tests/regressiontests/forms/localflavor/__init__.py @@ -1,11 +1 @@ -from django.forms import EmailField -from utils import LocalFlavorTestCase - -class AssertFieldOutputTests(LocalFlavorTestCase): - - def test_assert_field_output(self): - error_invalid = [u'Enter a valid e-mail address.'] - self.assertFieldOutput(EmailField, {'a@a.com': 'a@a.com'}, {'aaa': error_invalid}) - self.assertRaises(AssertionError, self.assertFieldOutput, EmailField, {'a@a.com': 'a@a.com'}, {'aaa': error_invalid + [u'Another error']}) - self.assertRaises(AssertionError, self.assertFieldOutput, EmailField, {'a@a.com': 'Wrong output'}, {'aaa': error_invalid}) - self.assertRaises(AssertionError, self.assertFieldOutput, EmailField, {'a@a.com': 'a@a.com'}, {'aaa': [u'Come on, gimme some well formatted data, dude.']}) +# diff --git a/tests/regressiontests/forms/localflavor/ar.py b/tests/regressiontests/forms/localflavor/ar.py index f0416e1a1d..fae78c9525 100644 --- a/tests/regressiontests/forms/localflavor/ar.py +++ b/tests/regressiontests/forms/localflavor/ar.py @@ -1,10 +1,10 @@ from django.contrib.localflavor.ar.forms import (ARProvinceSelect, ARPostalCodeField, ARDNIField, ARCUITField) -from utils import LocalFlavorTestCase +from django.test import SimpleTestCase -class ARLocalFlavorTests(LocalFlavorTestCase): +class ARLocalFlavorTests(SimpleTestCase): def test_ARProvinceSelect(self): f = ARProvinceSelect() out = u''' diff --git a/tests/regressiontests/forms/localflavor/au.py b/tests/regressiontests/forms/localflavor/au.py index ddf2b8eb6d..9a753d85e2 100644 --- a/tests/regressiontests/forms/localflavor/au.py +++ b/tests/regressiontests/forms/localflavor/au.py @@ -1,10 +1,10 @@ from django.contrib.localflavor.au.forms import (AUPostCodeField, AUPhoneNumberField, AUStateSelect) -from utils import LocalFlavorTestCase +from django.test import SimpleTestCase -class AULocalFlavorTests(LocalFlavorTestCase): +class AULocalFlavorTests(SimpleTestCase): def test_AUStateSelect(self): f = AUStateSelect() out = u''' diff --git a/tests/regressiontests/forms/localflavor/cl.py b/tests/regressiontests/forms/localflavor/cl.py index 59e4824db9..c6e75b19da 100644 --- a/tests/regressiontests/forms/localflavor/cl.py +++ b/tests/regressiontests/forms/localflavor/cl.py @@ -1,9 +1,9 @@ from django.contrib.localflavor.cl.forms import CLRutField, CLRegionSelect -from utils import LocalFlavorTestCase +from django.test import SimpleTestCase -class CLLocalFlavorTests(LocalFlavorTestCase): +class CLLocalFlavorTests(SimpleTestCase): def test_CLRegionSelect(self): f = CLRegionSelect() out = u''' diff --git a/tests/regressiontests/forms/localflavor/co.py b/tests/regressiontests/forms/localflavor/co.py index a6240a3146..bea7c8e137 100644 --- a/tests/regressiontests/forms/localflavor/co.py +++ b/tests/regressiontests/forms/localflavor/co.py @@ -1,8 +1,8 @@ from django.contrib.localflavor.co.forms import CODepartmentSelect -from utils import LocalFlavorTestCase +from django.test import SimpleTestCase -class COLocalFlavorTests(LocalFlavorTestCase): +class COLocalFlavorTests(SimpleTestCase): def test_CODepartmentSelect(self): d = CODepartmentSelect() out = u""" diff --git a/tests/regressiontests/forms/localflavor/ec.py b/tests/regressiontests/forms/localflavor/ec.py index 83537af70b..d324d0ca1e 100644 --- a/tests/regressiontests/forms/localflavor/ec.py +++ b/tests/regressiontests/forms/localflavor/ec.py @@ -1,8 +1,8 @@ from django.contrib.localflavor.ec.forms import ECProvinceSelect -from utils import LocalFlavorTestCase +from django.test import SimpleTestCase -class ECLocalFlavorTests(LocalFlavorTestCase): +class ECLocalFlavorTests(SimpleTestCase): def test_ECProvinceSelect(self): p = ECProvinceSelect() out = u""" diff --git a/tests/regressiontests/forms/localflavor/fi.py b/tests/regressiontests/forms/localflavor/fi.py index 3ad95e6148..e664e30af6 100644 --- a/tests/regressiontests/forms/localflavor/fi.py +++ b/tests/regressiontests/forms/localflavor/fi.py @@ -1,10 +1,10 @@ from django.contrib.localflavor.fi.forms import (FIZipCodeField, FISocialSecurityNumber, FIMunicipalitySelect) -from utils import LocalFlavorTestCase +from django.test import SimpleTestCase -class FILocalFlavorTests(LocalFlavorTestCase): +class FILocalFlavorTests(SimpleTestCase): def test_FIMunicipalitySelect(self): f = FIMunicipalitySelect() out = u''' diff --git a/tests/regressiontests/forms/localflavor/id.py b/tests/regressiontests/forms/localflavor/id.py index 71a8d9f8cf..d3c086289f 100644 --- a/tests/regressiontests/forms/localflavor/id.py +++ b/tests/regressiontests/forms/localflavor/id.py @@ -4,10 +4,10 @@ from django.contrib.localflavor.id.forms import (IDPhoneNumberField, IDPostCodeField, IDNationalIdentityNumberField, IDLicensePlateField, IDProvinceSelect, IDLicensePlatePrefixSelect) -from utils import LocalFlavorTestCase +from django.test import SimpleTestCase -class IDLocalFlavorTests(LocalFlavorTestCase): +class IDLocalFlavorTests(SimpleTestCase): def setUp(self): self.save_warnings_state() warnings.filterwarnings( diff --git a/tests/regressiontests/forms/localflavor/ie.py b/tests/regressiontests/forms/localflavor/ie.py index fab519bfe9..24556f7d0d 100644 --- a/tests/regressiontests/forms/localflavor/ie.py +++ b/tests/regressiontests/forms/localflavor/ie.py @@ -1,9 +1,9 @@ from django.contrib.localflavor.ie.forms import IECountySelect -from utils import LocalFlavorTestCase +from django.test import SimpleTestCase -class IELocalFlavorTests(LocalFlavorTestCase): +class IELocalFlavorTests(SimpleTestCase): def test_IECountySelect(self): f = IECountySelect() out = u''' diff --git a/tests/regressiontests/forms/localflavor/it.py b/tests/regressiontests/forms/localflavor/it.py index 7181e25e84..456a56bf34 100644 --- a/tests/regressiontests/forms/localflavor/it.py +++ b/tests/regressiontests/forms/localflavor/it.py @@ -1,10 +1,10 @@ from django.contrib.localflavor.it.forms import (ITZipCodeField, ITRegionSelect, ITSocialSecurityNumberField, ITVatNumberField) -from utils import LocalFlavorTestCase +from django.test import SimpleTestCase -class ITLocalFlavorTests(LocalFlavorTestCase): +class ITLocalFlavorTests(SimpleTestCase): def test_ITRegionSelect(self): f = ITRegionSelect() out = u''' diff --git a/tests/regressiontests/forms/localflavor/kw.py b/tests/regressiontests/forms/localflavor/kw.py index af998bd34b..3dfeef5469 100644 --- a/tests/regressiontests/forms/localflavor/kw.py +++ b/tests/regressiontests/forms/localflavor/kw.py @@ -1,9 +1,9 @@ from django.contrib.localflavor.kw.forms import KWCivilIDNumberField -from utils import LocalFlavorTestCase +from django.test import SimpleTestCase -class KWLocalFlavorTests(LocalFlavorTestCase): +class KWLocalFlavorTests(SimpleTestCase): def test_KWCivilIDNumberField(self): error_invalid = [u'Enter a valid Kuwaiti Civil ID number'] valid = { diff --git a/tests/regressiontests/forms/localflavor/mk.py b/tests/regressiontests/forms/localflavor/mk.py index 0a41e2001b..913dfb777d 100644 --- a/tests/regressiontests/forms/localflavor/mk.py +++ b/tests/regressiontests/forms/localflavor/mk.py @@ -1,10 +1,10 @@ from django.contrib.localflavor.mk.forms import ( MKIdentityCardNumberField, MKMunicipalitySelect, UMCNField) -from utils import LocalFlavorTestCase +from django.test import SimpleTestCase -class MKLocalFlavorTests(LocalFlavorTestCase): +class MKLocalFlavorTests(SimpleTestCase): def test_MKIdentityCardNumberField(self): error_invalid = [u'Identity card numbers must contain either 4 to 7 ' diff --git a/tests/regressiontests/forms/localflavor/mx.py b/tests/regressiontests/forms/localflavor/mx.py index 1ffc11c834..30937de665 100644 --- a/tests/regressiontests/forms/localflavor/mx.py +++ b/tests/regressiontests/forms/localflavor/mx.py @@ -2,10 +2,10 @@ from django.contrib.localflavor.mx.forms import (MXZipCodeField, MXRFCField, MXStateSelect, MXCURPField) -from utils import LocalFlavorTestCase +from django.test import SimpleTestCase -class MXLocalFlavorTests(LocalFlavorTestCase): +class MXLocalFlavorTests(SimpleTestCase): def test_MXStateSelect(self): f = MXStateSelect() out = u''' diff --git a/tests/regressiontests/forms/localflavor/pl.py b/tests/regressiontests/forms/localflavor/pl.py index 7225abe87d..8c7af38397 100644 --- a/tests/regressiontests/forms/localflavor/pl.py +++ b/tests/regressiontests/forms/localflavor/pl.py @@ -1,10 +1,10 @@ from django.contrib.localflavor.pl.forms import (PLProvinceSelect, PLCountySelect, PLPostalCodeField, PLNIPField, PLPESELField, PLNationalIDCardNumberField, PLREGONField) -from utils import LocalFlavorTestCase +from django.test import SimpleTestCase -class PLLocalFlavorTests(LocalFlavorTestCase): +class PLLocalFlavorTests(SimpleTestCase): def test_PLProvinceSelect(self): f = PLProvinceSelect() out = u''' diff --git a/tests/regressiontests/forms/localflavor/ro.py b/tests/regressiontests/forms/localflavor/ro.py index 94e6652af8..731157ca69 100644 --- a/tests/regressiontests/forms/localflavor/ro.py +++ b/tests/regressiontests/forms/localflavor/ro.py @@ -3,10 +3,10 @@ from django.contrib.localflavor.ro.forms import (ROCIFField, ROCNPField, ROCountyField, ROCountySelect, ROIBANField, ROPhoneNumberField, ROPostalCodeField) -from utils import LocalFlavorTestCase +from django.test import SimpleTestCase -class ROLocalFlavorTests(LocalFlavorTestCase): +class ROLocalFlavorTests(SimpleTestCase): def test_ROCountySelect(self): f = ROCountySelect() out = u''' diff --git a/tests/regressiontests/forms/localflavor/us.py b/tests/regressiontests/forms/localflavor/us.py index 8924f0b2da..9b38db5b7c 100644 --- a/tests/regressiontests/forms/localflavor/us.py +++ b/tests/regressiontests/forms/localflavor/us.py @@ -1,10 +1,10 @@ from django.contrib.localflavor.us.forms import (USZipCodeField, USPhoneNumberField, USStateField, USStateSelect, USSocialSecurityNumberField) -from utils import LocalFlavorTestCase +from django.test import SimpleTestCase -class USLocalFlavorTests(LocalFlavorTestCase): +class USLocalFlavorTests(SimpleTestCase): def test_USStateSelect(self): f = USStateSelect() out = u''' diff --git a/tests/regressiontests/forms/localflavor/za.py b/tests/regressiontests/forms/localflavor/za.py index c912421220..948bd7f320 100644 --- a/tests/regressiontests/forms/localflavor/za.py +++ b/tests/regressiontests/forms/localflavor/za.py @@ -1,9 +1,9 @@ from django.contrib.localflavor.za.forms import ZAIDField, ZAPostCodeField -from utils import LocalFlavorTestCase +from django.test import SimpleTestCase -class ZALocalFlavorTests(LocalFlavorTestCase): +class ZALocalFlavorTests(SimpleTestCase): def test_ZAIDField(self): error_invalid = [u'Enter a valid South African ID number'] valid = { diff --git a/tests/regressiontests/forms/localflavortests.py b/tests/regressiontests/forms/localflavortests.py index a4db3dba98..6a3fb2322e 100644 --- a/tests/regressiontests/forms/localflavortests.py +++ b/tests/regressiontests/forms/localflavortests.py @@ -1,4 +1,3 @@ -from localflavor import AssertFieldOutputTests from localflavor.ar import ARLocalFlavorTests from localflavor.at import ATLocalFlavorTests from localflavor.au import AULocalFlavorTests diff --git a/tests/regressiontests/forms/tests/__init__.py b/tests/regressiontests/forms/tests/__init__.py index f63818d578..9570835aa9 100644 --- a/tests/regressiontests/forms/tests/__init__.py +++ b/tests/regressiontests/forms/tests/__init__.py @@ -53,5 +53,4 @@ from regressiontests.forms.localflavortests import ( USLocalFlavorTests, UYLocalFlavorTests, ZALocalFlavorTests, - AssertFieldOutputTests, ) diff --git a/tests/regressiontests/test_utils/tests.py b/tests/regressiontests/test_utils/tests.py index 6d4ab02247..942aa85148 100644 --- a/tests/regressiontests/test_utils/tests.py +++ b/tests/regressiontests/test_utils/tests.py @@ -1,5 +1,6 @@ from __future__ import with_statement +from django.forms import EmailField from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature from django.utils.unittest import skip @@ -139,6 +140,16 @@ class AssertRaisesMsgTest(SimpleTestCase): self.assertRaisesMessage(ValueError, "[.*x+]y?", func1) +class AssertFieldOutputTests(SimpleTestCase): + + def test_assert_field_output(self): + error_invalid = [u'Enter a valid e-mail address.'] + self.assertFieldOutput(EmailField, {'a@a.com': 'a@a.com'}, {'aaa': error_invalid}) + self.assertRaises(AssertionError, self.assertFieldOutput, EmailField, {'a@a.com': 'a@a.com'}, {'aaa': error_invalid + [u'Another error']}) + self.assertRaises(AssertionError, self.assertFieldOutput, EmailField, {'a@a.com': 'Wrong output'}, {'aaa': error_invalid}) + self.assertRaises(AssertionError, self.assertFieldOutput, EmailField, {'a@a.com': 'a@a.com'}, {'aaa': [u'Come on, gimme some well formatted data, dude.']}) + + __test__ = {"API_TEST": r""" # Some checks of the doctest output normalizer. # Standard doctests do fairly