Browse files

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

Thank-you Martin Green for the patch.
  • Loading branch information...
timgraham committed Feb 8, 2013
1 parent 1e29428 commit d194714c0a707773bd470bffb3d67a60e40bb787
@@ -253,6 +253,7 @@ answer newbie questions, and generally made Django that much better:
Collin Grady <>
Gabriel Grant <>
Martin Green
Daniel Greenfeld
Simon Greenhill <>
Owen Griffiths
@@ -509,6 +509,83 @@ def assertFormError(self, response, form, field, errors, msg_prefix=''): + "The form '%s' was not used to render the"
" response" % form)
def assertFormsetError(self, response, formset, form_index, field, errors,
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
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: + '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:
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,
elif field in context[formset].forms[form_index].fields: + "The field '%s' "
"on formset '%s', form %d in "
"context %d contains no errors" %
(field, formset, form_index, i))
else: + "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,
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: + "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
@@ -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:
Backwards incompatible changes in 1.6
@@ -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
``formset`` is the name the ``Formset`` instance was given in the template
``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
@@ -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),
@@ -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
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."
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()
t = Template('Invalid POST data. {{ my_formset.errors }}',
name='Invalid POST Template')
c = Context({'my_formset': formset})
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')
Oops, something went wrong.

0 comments on commit d194714

Please sign in to comment.