Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixed #11603 - Added django.test.SimpleTestCase.assertFormsetError

Thank-you Martin Green for the patch.
  • Loading branch information...
commit d194714c0a707773bd470bffb3d67a60e40bb787 1 parent 1e29428
@timgraham timgraham authored
View
1  AUTHORS
@@ -253,6 +253,7 @@ answer newbie questions, and generally made Django that much better:
pradeep.gowda@gmail.com
Collin Grady <collin@collingrady.com>
Gabriel Grant <g@briel.ca>
+ Martin Green
Daniel Greenfeld
Simon Greenhill <dev@simon.net.nz>
Owen Griffiths
View
77 django/test/testcases.py
@@ -509,6 +509,83 @@ def assertFormError(self, response, form, field, errors, msg_prefix=''):
self.fail(msg_prefix + "The form '%s' was not used to render the"
" response" % form)
+ def assertFormsetError(self, response, formset, form_index, field, errors,
+ msg_prefix=''):
+ """
+ Asserts that a formset used to render the response has a specific error.
+
+ For field errors, specify the ``form_index`` and the ``field``.
+ For non-field errors, specify the ``form_index`` and the ``field`` as
+ None.
+ For non-form errors, specify ``form_index`` as None and the ``field``
+ as None.
+ """
+ # Add punctuation to msg_prefix
+ if msg_prefix:
+ msg_prefix += ": "
+
+ # Put context(s) into a list to simplify processing.
+ contexts = to_list(response.context)
+ if not contexts:
+ self.fail(msg_prefix + 'Response did not use any contexts to '
+ 'render the response')
+
+ # Put error(s) into a list to simplify processing.
+ errors = to_list(errors)
+
+ # Search all contexts for the error.
+ found_formset = False
+ for i, context in enumerate(contexts):
+ if formset not in context:
+ continue
+ found_formset = True
+ for err in errors:
+ if field is not None:
+ if field in context[formset].forms[form_index].errors:
+ field_errors = context[formset].forms[form_index].errors[field]
+ self.assertTrue(err in field_errors,
+ msg_prefix + "The field '%s' on formset '%s', "
+ "form %d in context %d does not contain the "
+ "error '%s' (actual errors: %s)" %
+ (field, formset, form_index, i, err,
+ repr(field_errors)))
+ elif field in context[formset].forms[form_index].fields:
+ self.fail(msg_prefix + "The field '%s' "
+ "on formset '%s', form %d in "
+ "context %d contains no errors" %
+ (field, formset, form_index, i))
+ else:
+ self.fail(msg_prefix + "The formset '%s', form %d in "
+ "context %d does not contain the field '%s'" %
+ (formset, form_index, i, field))
+ elif form_index is not None:
+ non_field_errors = context[formset].forms[form_index].non_field_errors()
+ self.assertFalse(len(non_field_errors) == 0,
+ msg_prefix + "The formset '%s', form %d in "
+ "context %d does not contain any non-field "
+ "errors." % (formset, form_index, i))
+ self.assertTrue(err in non_field_errors,
+ msg_prefix + "The formset '%s', form %d "
+ "in context %d does not contain the "
+ "non-field error '%s' "
+ "(actual errors: %s)" %
+ (formset, form_index, i, err,
+ repr(non_field_errors)))
+ else:
+ non_form_errors = context[formset].non_form_errors()
+ self.assertFalse(len(non_form_errors) == 0,
+ msg_prefix + "The formset '%s' in "
+ "context %d does not contain any "
+ "non-form errors." % (formset, i))
+ self.assertTrue(err in non_form_errors,
+ msg_prefix + "The formset '%s' in context "
+ "%d does not contain the "
+ "non-form error '%s' (actual errors: %s)" %
+ (formset, i, err, repr(non_form_errors)))
+ if not found_formset:
+ self.fail(msg_prefix + "The formset '%s' was not used to render "
+ "the response" % formset)
+
def assertTemplateUsed(self, response=None, template_name=None, msg_prefix=''):
"""
Asserts that the template with the provided name was used in rendering
View
4 docs/releases/1.6.txt
@@ -283,6 +283,10 @@ Minor features
* The :meth:`~django.db.models.query.QuerySet.get_or_create` method no longer
requires at least one keyword argument.
+* The :class:`~django.test.SimpleTestCase` class includes a new assertion
+ helper for testing formset errors:
+ :meth:`~django.test.SimpleTestCase.assertFormsetError`.
+
Backwards incompatible changes in 1.6
=====================================
View
21 docs/topics/testing/overview.txt
@@ -1532,6 +1532,27 @@ your test suite.
``errors`` is an error string, or a list of error strings, that are
expected as a result of form validation.
+.. method:: SimpleTestCase.assertFormsetError(response, formset, form_index, field, errors, msg_prefix='')
+
+ .. versionadded:: 1.6
+
+ Asserts that the ``formset`` raises the provided list of errors when
+ rendered.
+
+ ``formset`` is the name the ``Formset`` instance was given in the template
+ context.
+
+ ``form_index`` is the number of the form within the ``Formset``. If
+ ``form_index`` has a value of ``None``, non-form errors (errors you can
+ access via ``formset.non_form_errors()``) will be checked.
+
+ ``field`` is the name of the field on the form to check. If ``field``
+ has a value of ``None``, non-field errors (errors you can access via
+ ``form.non_field_errors()``) will be checked.
+
+ ``errors`` is an error string, or a list of error strings, that are
+ expected as a result of form validation.
+
.. 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
View
1  tests/test_client/urls.py
@@ -21,6 +21,7 @@
(r'^bad_view/$', views.bad_view),
(r'^form_view/$', views.form_view),
(r'^form_view_with_template/$', views.form_view_with_template),
+ (r'^formset_view/$', views.formset_view),
(r'^login_protected_view/$', views.login_protected_view),
(r'^login_protected_method_view/$', views.login_protected_method_view),
(r'^login_protected_view_custom_redirect/$', views.login_protected_view_changed_redirect),
View
46 tests/test_client/views.py
@@ -7,7 +7,8 @@
from django.contrib.auth.decorators import login_required, permission_required
from django.core import mail
from django.forms import fields
-from django.forms.forms import Form
+from django.forms.forms import Form, ValidationError
+from django.forms.formsets import formset_factory, BaseFormSet
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseNotFound
from django.shortcuts import render_to_response
from django.template import Context, Template
@@ -95,6 +96,12 @@ class TestForm(Form):
single = fields.ChoiceField(choices=TestChoices)
multi = fields.MultipleChoiceField(choices=TestChoices)
+ def clean(self):
+ cleaned_data = self.cleaned_data
+ if cleaned_data.get("text") == "Raise non-field error":
+ raise ValidationError("Non-field error.")
+ return cleaned_data
+
def form_view(request):
"A view that tests a simple form"
if request.method == 'POST':
@@ -130,6 +137,43 @@ def form_view_with_template(request):
}
)
+class BaseTestFormSet(BaseFormSet):
+ def clean(self):
+ """Checks that no two email addresses are the same."""
+ if any(self.errors):
+ # Don't bother validating the formset unless each form is valid
+ return
+
+ emails = []
+ for i in range(0, self.total_form_count()):
+ form = self.forms[i]
+ email = form.cleaned_data['email']
+ if email in emails:
+ raise ValidationError(
+ "Forms in a set must have distinct email addresses."
+ )
+ emails.append(email)
+
+TestFormSet = formset_factory(TestForm, BaseTestFormSet)
+
+def formset_view(request):
+ "A view that tests a simple formset"
+ if request.method == 'POST':
+ formset = TestFormSet(request.POST)
+ if formset.is_valid():
+ t = Template('Valid POST data.', name='Valid POST Template')
+ c = Context()
+ else:
+ t = Template('Invalid POST data. {{ my_formset.errors }}',
+ name='Invalid POST Template')
+ c = Context({'my_formset': formset})
+ else:
+ formset = TestForm(request.GET)
+ t = Template('Viewing base formset. {{ my_formset }}.',
+ name='Formset GET Template')
+ c = Context({'my_formset': formset})
+ return HttpResponse(t.render(c))
+
def login_protected_view(request):
"A simple view that is login protected."
t = Template('This is a login protected test. Username is {{ user.username }}.', name='Login Template')
View
191 tests/test_client_regress/tests.py
@@ -543,6 +543,197 @@ def test_unknown_nonfield_error(self):
except AssertionError as e:
self.assertIn("abc: The form 'form' in context 0 does not contain the non-field error 'Some error.' (actual errors: )", str(e))
+class AssertFormsetErrorTests(TestCase):
+ msg_prefixes = [("", {}), ("abc: ", {"msg_prefix": "abc"})]
+ def setUp(self):
+ """Makes response object for testing field and non-field errors"""
+ # For testing field and non-field errors
+ self.response_form_errors = self.getResponse({
+ 'form-TOTAL_FORMS': '2',
+ 'form-INITIAL_FORMS': '2',
+ 'form-0-text': 'Raise non-field error',
+ 'form-0-email': 'not an email address',
+ 'form-0-value': 37,
+ 'form-0-single': 'b',
+ 'form-0-multi': ('b','c','e'),
+ 'form-1-text': 'Hello World',
+ 'form-1-email': 'email@domain.com',
+ 'form-1-value': 37,
+ 'form-1-single': 'b',
+ 'form-1-multi': ('b','c','e'),
+ })
+ # For testing non-form errors
+ self.response_nonform_errors = self.getResponse({
+ 'form-TOTAL_FORMS': '2',
+ 'form-INITIAL_FORMS': '2',
+ 'form-0-text': 'Hello World',
+ 'form-0-email': 'email@domain.com',
+ 'form-0-value': 37,
+ 'form-0-single': 'b',
+ 'form-0-multi': ('b','c','e'),
+ 'form-1-text': 'Hello World',
+ 'form-1-email': 'email@domain.com',
+ 'form-1-value': 37,
+ 'form-1-single': 'b',
+ 'form-1-multi': ('b','c','e'),
+ })
+
+ def getResponse(self, post_data):
+ response = self.client.post('/test_client/formset_view/', post_data)
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, "Invalid POST Template")
+ return response
+
+ def test_unknown_formset(self):
+ "An assertion is raised if the formset name is unknown"
+ for prefix, kwargs in self.msg_prefixes:
+ with self.assertRaises(AssertionError) as cm:
+ self.assertFormsetError(self.response_form_errors,
+ 'wrong_formset',
+ 0,
+ 'Some_field',
+ 'Some error.',
+ **kwargs)
+ self.assertIn(prefix + "The formset 'wrong_formset' was not "
+ "used to render the response",
+ str(cm.exception))
+
+ def test_unknown_field(self):
+ "An assertion is raised if the field name is unknown"
+ for prefix, kwargs in self.msg_prefixes:
+ with self.assertRaises(AssertionError) as cm:
+ self.assertFormsetError(self.response_form_errors,
+ 'my_formset',
+ 0,
+ 'Some_field',
+ 'Some error.',
+ **kwargs)
+ self.assertIn(prefix + "The formset 'my_formset', "
+ "form 0 in context 0 "
+ "does not contain the field 'Some_field'",
+ str(cm.exception))
+
+ def test_no_error_field(self):
+ "An assertion is raised if the field doesn't have any errors"
+ for prefix, kwargs in self.msg_prefixes:
+ with self.assertRaises(AssertionError) as cm:
+ self.assertFormsetError(self.response_form_errors,
+ 'my_formset',
+ 1,
+ 'value',
+ 'Some error.',
+ **kwargs)
+ self.assertIn(prefix + "The field 'value' "
+ "on formset 'my_formset', form 1 "
+ "in context 0 contains no errors",
+ str(cm.exception))
+
+ def test_unknown_error(self):
+ "An assertion is raised if the field doesn't contain the specified error"
+ for prefix, kwargs in self.msg_prefixes:
+ with self.assertRaises(AssertionError) as cm:
+ self.assertFormsetError(self.response_form_errors,
+ 'my_formset',
+ 0,
+ 'email',
+ 'Some error.',
+ **kwargs)
+ self.assertIn(str_prefix(prefix + "The field 'email' "
+ "on formset 'my_formset', form 0 in context 0 does not "
+ "contain the error 'Some error.' (actual errors: "
+ "[%(_)s'Enter a valid email address.'])"),
+ str(cm.exception))
+
+ def test_field_error(self):
+ "No assertion is raised if the field contains the provided error"
+ for prefix, kwargs in self.msg_prefixes:
+ self.assertFormsetError(self.response_form_errors,
+ 'my_formset',
+ 0,
+ 'email',
+ ['Enter a valid email address.'],
+ **kwargs)
+
+ def test_no_nonfield_error(self):
+ "An assertion is raised if the formsets non-field errors doesn't contain any errors."
+ for prefix, kwargs in self.msg_prefixes:
+ with self.assertRaises(AssertionError) as cm:
+ self.assertFormsetError(self.response_form_errors,
+ 'my_formset',
+ 1,
+ None,
+ 'Some error.',
+ **kwargs)
+ self.assertIn(prefix + "The formset 'my_formset', form 1 in "
+ "context 0 does not contain any "
+ "non-field errors.",
+ str(cm.exception))
+
+ def test_unknown_nonfield_error(self):
+ "An assertion is raised if the formsets non-field errors doesn't contain the provided error."
+ for prefix, kwargs in self.msg_prefixes:
+ with self.assertRaises(AssertionError) as cm:
+ self.assertFormsetError(self.response_form_errors,
+ 'my_formset',
+ 0,
+ None,
+ 'Some error.',
+ **kwargs)
+ self.assertIn(str_prefix(prefix +
+ "The formset 'my_formset', form 0 in context 0 does not "
+ "contain the non-field error 'Some error.' (actual errors: "
+ "[%(_)s'Non-field error.'])"), str(cm.exception))
+
+ def test_nonfield_error(self):
+ "No assertion is raised if the formsets non-field errors contains the provided error."
+ for prefix, kwargs in self.msg_prefixes:
+ self.assertFormsetError(self.response_form_errors,
+ 'my_formset',
+ 0,
+ None,
+ 'Non-field error.',
+ **kwargs)
+
+ def test_no_nonform_error(self):
+ "An assertion is raised if the formsets non-form errors doesn't contain any errors."
+ for prefix, kwargs in self.msg_prefixes:
+ with self.assertRaises(AssertionError) as cm:
+ self.assertFormsetError(self.response_form_errors,
+ 'my_formset',
+ None,
+ None,
+ 'Some error.',
+ **kwargs)
+ self.assertIn(prefix + "The formset 'my_formset' in context 0 "
+ "does not contain any non-form errors.",
+ str(cm.exception))
+
+ def test_unknown_nonform_error(self):
+ "An assertion is raised if the formsets non-form errors doesn't contain the provided error."
+ for prefix, kwargs in self.msg_prefixes:
+ with self.assertRaises(AssertionError) as cm:
+ self.assertFormsetError(self.response_nonform_errors,
+ 'my_formset',
+ None,
+ None,
+ 'Some error.',
+ **kwargs)
+ self.assertIn(str_prefix(prefix +
+ "The formset 'my_formset' in context 0 does not contain the "
+ "non-form error 'Some error.' (actual errors: [%(_)s'Forms "
+ "in a set must have distinct email addresses.'])"), str(cm.exception))
+
+ def test_nonform_error(self):
+ "No assertion is raised if the formsets non-form errors contains the provided error."
+ for prefix, kwargs in self.msg_prefixes:
+ self.assertFormsetError(self.response_nonform_errors,
+ 'my_formset',
+ None,
+ None,
+ 'Forms in a set must have distinct email '
+ 'addresses.',
+ **kwargs)
+
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
class LoginTests(TestCase):
fixtures = ['testdata']
Please sign in to comment.
Something went wrong with that request. Please try again.