mirror of https://github.com/django/django.git
Fixed #11603 - Added django.test.SimpleTestCase.assertFormsetError
Thank-you Martin Green for the patch.
This commit is contained in:
parent
1e29428db2
commit
d194714c0a
1
AUTHORS
1
AUTHORS
|
@ -253,6 +253,7 @@ answer newbie questions, and generally made Django that much better:
|
||||||
pradeep.gowda@gmail.com
|
pradeep.gowda@gmail.com
|
||||||
Collin Grady <collin@collingrady.com>
|
Collin Grady <collin@collingrady.com>
|
||||||
Gabriel Grant <g@briel.ca>
|
Gabriel Grant <g@briel.ca>
|
||||||
|
Martin Green
|
||||||
Daniel Greenfeld
|
Daniel Greenfeld
|
||||||
Simon Greenhill <dev@simon.net.nz>
|
Simon Greenhill <dev@simon.net.nz>
|
||||||
Owen Griffiths
|
Owen Griffiths
|
||||||
|
|
|
@ -509,6 +509,83 @@ class SimpleTestCase(ut2.TestCase):
|
||||||
self.fail(msg_prefix + "The form '%s' was not used to render the"
|
self.fail(msg_prefix + "The form '%s' was not used to render the"
|
||||||
" response" % form)
|
" 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=''):
|
def assertTemplateUsed(self, response=None, template_name=None, msg_prefix=''):
|
||||||
"""
|
"""
|
||||||
Asserts that the template with the provided name was used in rendering
|
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
|
* The :meth:`~django.db.models.query.QuerySet.get_or_create` method no longer
|
||||||
requires at least one keyword argument.
|
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
|
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
|
``errors`` is an error string, or a list of error strings, that are
|
||||||
expected as a result of form validation.
|
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)
|
.. 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
|
Asserts that a ``Response`` instance produced the given ``status_code`` and
|
||||||
|
|
|
@ -21,6 +21,7 @@ urlpatterns = patterns('',
|
||||||
(r'^bad_view/$', views.bad_view),
|
(r'^bad_view/$', views.bad_view),
|
||||||
(r'^form_view/$', views.form_view),
|
(r'^form_view/$', views.form_view),
|
||||||
(r'^form_view_with_template/$', views.form_view_with_template),
|
(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_view/$', views.login_protected_view),
|
||||||
(r'^login_protected_method_view/$', views.login_protected_method_view),
|
(r'^login_protected_method_view/$', views.login_protected_method_view),
|
||||||
(r'^login_protected_view_custom_redirect/$', views.login_protected_view_changed_redirect),
|
(r'^login_protected_view_custom_redirect/$', views.login_protected_view_changed_redirect),
|
||||||
|
|
|
@ -7,7 +7,8 @@ from xml.dom.minidom import parseString
|
||||||
from django.contrib.auth.decorators import login_required, permission_required
|
from django.contrib.auth.decorators import login_required, permission_required
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
from django.forms import fields
|
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.http import HttpResponse, HttpResponseRedirect, HttpResponseNotFound
|
||||||
from django.shortcuts import render_to_response
|
from django.shortcuts import render_to_response
|
||||||
from django.template import Context, Template
|
from django.template import Context, Template
|
||||||
|
@ -95,6 +96,12 @@ class TestForm(Form):
|
||||||
single = fields.ChoiceField(choices=TestChoices)
|
single = fields.ChoiceField(choices=TestChoices)
|
||||||
multi = fields.MultipleChoiceField(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):
|
def form_view(request):
|
||||||
"A view that tests a simple form"
|
"A view that tests a simple form"
|
||||||
if request.method == 'POST':
|
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):
|
def login_protected_view(request):
|
||||||
"A simple view that is login protected."
|
"A simple view that is login protected."
|
||||||
t = Template('This is a login protected test. Username is {{ user.username }}.', name='Login Template')
|
t = Template('This is a login protected test. Username is {{ user.username }}.', name='Login Template')
|
||||||
|
|
|
@ -543,6 +543,197 @@ class AssertFormErrorTests(TestCase):
|
||||||
except AssertionError as e:
|
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))
|
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',))
|
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',))
|
||||||
class LoginTests(TestCase):
|
class LoginTests(TestCase):
|
||||||
fixtures = ['testdata']
|
fixtures = ['testdata']
|
||||||
|
|
Loading…
Reference in New Issue