mirror of https://github.com/django/django.git
Refs #33348 -- Removed support for passing response object and form/formset name to SimpleTestCase.assertFormError()/assertFormSetError().
Per deprecation timeline.
This commit is contained in:
parent
d6816bff73
commit
71d1203b07
|
@ -1,5 +1,4 @@
|
|||
import difflib
|
||||
import inspect
|
||||
import json
|
||||
import logging
|
||||
import posixpath
|
||||
|
@ -42,7 +41,6 @@ from django.db import DEFAULT_DB_ALIAS, connection, connections, transaction
|
|||
from django.forms.fields import CharField
|
||||
from django.http import QueryDict
|
||||
from django.http.request import split_domain_port, validate_host
|
||||
from django.http.response import HttpResponseBase
|
||||
from django.test.client import AsyncClient, Client
|
||||
from django.test.html import HTMLParseError, parse_html
|
||||
from django.test.signals import template_rendered
|
||||
|
@ -53,7 +51,7 @@ from django.test.utils import (
|
|||
modify_settings,
|
||||
override_settings,
|
||||
)
|
||||
from django.utils.deprecation import RemovedInDjango50Warning, RemovedInDjango51Warning
|
||||
from django.utils.deprecation import RemovedInDjango51Warning
|
||||
from django.utils.functional import classproperty
|
||||
from django.utils.version import PY310
|
||||
from django.views.static import serve
|
||||
|
@ -168,127 +166,6 @@ class _DatabaseFailure:
|
|||
raise DatabaseOperationForbidden(self.message)
|
||||
|
||||
|
||||
# RemovedInDjango50Warning
|
||||
class _AssertFormErrorDeprecationHelper:
|
||||
@staticmethod
|
||||
def assertFormError(self, response, form, field, errors, msg_prefix=""):
|
||||
"""
|
||||
Search through all the rendered contexts of the `response` for a form named
|
||||
`form` then dispatch to the new assertFormError() using that instance.
|
||||
If multiple contexts contain the form, they're all checked in order and any
|
||||
failure will abort (this matches the old behavior).
|
||||
"""
|
||||
warning_msg = (
|
||||
f"Passing response to assertFormError() is deprecated. Use the form object "
|
||||
f"directly: assertFormError(response.context[{form!r}], {field!r}, ...)"
|
||||
)
|
||||
warnings.warn(warning_msg, RemovedInDjango50Warning, stacklevel=2)
|
||||
|
||||
full_msg_prefix = f"{msg_prefix}: " if msg_prefix else ""
|
||||
contexts = to_list(response.context) if response.context is not None else []
|
||||
if not contexts:
|
||||
self.fail(
|
||||
f"{full_msg_prefix}Response did not use any contexts to render the "
|
||||
f"response"
|
||||
)
|
||||
# Search all contexts for the error.
|
||||
found_form = False
|
||||
for i, context in enumerate(contexts):
|
||||
if form not in context:
|
||||
continue
|
||||
found_form = True
|
||||
self.assertFormError(context[form], field, errors, msg_prefix=msg_prefix)
|
||||
if not found_form:
|
||||
self.fail(
|
||||
f"{full_msg_prefix}The form '{form}' was not used to render the "
|
||||
f"response"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def assertFormSetError(
|
||||
self, response, formset, form_index, field, errors, msg_prefix=""
|
||||
):
|
||||
"""
|
||||
Search for a formset named "formset" in the "response" and dispatch to
|
||||
the new assertFormSetError() using that instance. If the name is found
|
||||
in multiple contexts they're all checked in order and any failure will
|
||||
abort the test.
|
||||
"""
|
||||
warning_msg = (
|
||||
f"Passing response to assertFormSetError() is deprecated. Use the formset "
|
||||
f"object directly: assertFormSetError(response.context[{formset!r}], "
|
||||
f"{form_index!r}, ...)"
|
||||
)
|
||||
warnings.warn(warning_msg, RemovedInDjango50Warning, stacklevel=2)
|
||||
|
||||
full_msg_prefix = f"{msg_prefix}: " if msg_prefix else ""
|
||||
contexts = to_list(response.context) if response.context is not None else []
|
||||
if not contexts:
|
||||
self.fail(
|
||||
f"{full_msg_prefix}Response did not use any contexts to render the "
|
||||
f"response"
|
||||
)
|
||||
found_formset = False
|
||||
for i, context in enumerate(contexts):
|
||||
if formset not in context or not hasattr(context[formset], "forms"):
|
||||
continue
|
||||
found_formset = True
|
||||
self.assertFormSetError(
|
||||
context[formset], form_index, field, errors, msg_prefix
|
||||
)
|
||||
if not found_formset:
|
||||
self.fail(
|
||||
f"{full_msg_prefix}The formset '{formset}' was not used to render the "
|
||||
f"response"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def patch_signature(cls, new_method):
|
||||
"""
|
||||
Replace the decorated method with a new one that inspects the passed
|
||||
args/kwargs and dispatch to the old implementation (with deprecation
|
||||
warning) when it detects the old signature.
|
||||
"""
|
||||
|
||||
@wraps(new_method)
|
||||
def patched_method(self, *args, **kwargs):
|
||||
old_method = getattr(cls, new_method.__name__)
|
||||
old_signature = inspect.signature(old_method)
|
||||
try:
|
||||
old_bound_args = old_signature.bind(self, *args, **kwargs)
|
||||
except TypeError:
|
||||
# If old signature doesn't match then either:
|
||||
# 1) new signature will match
|
||||
# 2) or a TypeError will be raised showing the user information
|
||||
# about the new signature.
|
||||
return new_method(self, *args, **kwargs)
|
||||
|
||||
new_signature = inspect.signature(new_method)
|
||||
try:
|
||||
new_bound_args = new_signature.bind(self, *args, **kwargs)
|
||||
except TypeError:
|
||||
# Old signature matches but not the new one (because of
|
||||
# previous try/except).
|
||||
return old_method(self, *args, **kwargs)
|
||||
|
||||
# If both signatures match, decide on which method to call by
|
||||
# inspecting the first arg (arg[0] = self).
|
||||
assert old_bound_args.args[1] == new_bound_args.args[1]
|
||||
if hasattr(
|
||||
old_bound_args.args[1], "context"
|
||||
): # Looks like a response object => old method.
|
||||
return old_method(self, *args, **kwargs)
|
||||
elif isinstance(old_bound_args.args[1], HttpResponseBase):
|
||||
raise ValueError(
|
||||
f"{old_method.__name__}() is only usable on responses fetched "
|
||||
f"using the Django test Client."
|
||||
)
|
||||
else:
|
||||
return new_method(self, *args, **kwargs)
|
||||
|
||||
return patched_method
|
||||
|
||||
|
||||
class SimpleTestCase(unittest.TestCase):
|
||||
|
||||
# The class we'll use for the test client self.client.
|
||||
|
@ -711,9 +588,6 @@ class SimpleTestCase(unittest.TestCase):
|
|||
|
||||
self.assertEqual(field_errors, errors, msg_prefix + failure_message)
|
||||
|
||||
# RemovedInDjango50Warning: When the deprecation ends, remove the
|
||||
# decorator.
|
||||
@_AssertFormErrorDeprecationHelper.patch_signature
|
||||
def assertFormError(self, form, field, errors, msg_prefix=""):
|
||||
"""
|
||||
Assert that a field named "field" on the given form object has specific
|
||||
|
@ -738,9 +612,6 @@ class SimpleTestCase(unittest.TestCase):
|
|||
)
|
||||
return self.assertFormSetError(*args, **kw)
|
||||
|
||||
# RemovedInDjango50Warning: When the deprecation ends, remove the
|
||||
# decorator.
|
||||
@_AssertFormErrorDeprecationHelper.patch_signature
|
||||
def assertFormSetError(self, formset, form_index, field, errors, msg_prefix=""):
|
||||
"""
|
||||
Similar to assertFormError() but for formsets.
|
||||
|
|
|
@ -334,3 +334,7 @@ to remove usage of these features.
|
|||
|
||||
* The ``django.utils.timezone.utc`` alias to ``datetime.timezone.utc`` is
|
||||
removed.
|
||||
|
||||
* Passing a response object and a form/formset name to
|
||||
``SimpleTestCase.assertFormError()`` and ``assertFormSetError()`` is no
|
||||
longer allowed.
|
||||
|
|
|
@ -1601,12 +1601,6 @@ your test suite.
|
|||
which means that ``errors='error message'`` is the same as
|
||||
``errors=['error message']``.
|
||||
|
||||
.. deprecated:: 4.1
|
||||
|
||||
Support for passing a response object and a form name to
|
||||
``assertFormError()`` is deprecated and will be removed in Django 5.0.
|
||||
Use the form instance directly instead.
|
||||
|
||||
.. method:: SimpleTestCase.assertFormSetError(formset, form_index, field, errors, msg_prefix='')
|
||||
|
||||
Asserts that the ``formset`` raises the provided list of errors when
|
||||
|
@ -1624,12 +1618,6 @@ your test suite.
|
|||
``field`` and ``errors`` have the same meaning as the parameters to
|
||||
``assertFormError()``.
|
||||
|
||||
.. deprecated:: 4.1
|
||||
|
||||
Support for passing a response object and a formset name to
|
||||
``assertFormSetError()`` is deprecated and will be removed in Django
|
||||
5.0. Use the formset instance directly instead.
|
||||
|
||||
.. deprecated:: 4.2
|
||||
|
||||
The ``assertFormsetError()`` assertion method is deprecated. Use
|
||||
|
|
|
@ -46,7 +46,7 @@ from django.test.utils import (
|
|||
setup_test_environment,
|
||||
)
|
||||
from django.urls import NoReverseMatch, path, reverse, reverse_lazy
|
||||
from django.utils.deprecation import RemovedInDjango50Warning, RemovedInDjango51Warning
|
||||
from django.utils.deprecation import RemovedInDjango51Warning
|
||||
from django.utils.log import DEFAULT_LOGGING
|
||||
from django.utils.version import PY311
|
||||
|
||||
|
@ -1383,44 +1383,6 @@ class TestFormset(formset_factory(TestForm)):
|
|||
|
||||
|
||||
class AssertFormErrorTests(SimpleTestCase):
|
||||
@ignore_warnings(category=RemovedInDjango50Warning)
|
||||
def test_non_client_response(self):
|
||||
msg = (
|
||||
"assertFormError() is only usable on responses fetched using the "
|
||||
"Django test Client."
|
||||
)
|
||||
response = HttpResponse()
|
||||
with self.assertRaisesMessage(ValueError, msg):
|
||||
self.assertFormError(response, "form", "field", "invalid value")
|
||||
|
||||
@ignore_warnings(category=RemovedInDjango50Warning)
|
||||
def test_response_with_no_context(self):
|
||||
msg = "Response did not use any contexts to render the response"
|
||||
response = mock.Mock(context=[])
|
||||
with self.assertRaisesMessage(AssertionError, msg):
|
||||
self.assertFormError(response, "form", "field", "invalid value")
|
||||
msg_prefix = "Custom prefix"
|
||||
with self.assertRaisesMessage(AssertionError, f"{msg_prefix}: {msg}"):
|
||||
self.assertFormError(
|
||||
response,
|
||||
"form",
|
||||
"field",
|
||||
"invalid value",
|
||||
msg_prefix=msg_prefix,
|
||||
)
|
||||
|
||||
@ignore_warnings(category=RemovedInDjango50Warning)
|
||||
def test_form_not_in_context(self):
|
||||
msg = "The form 'form' was not used to render the response"
|
||||
response = mock.Mock(context=[{}])
|
||||
with self.assertRaisesMessage(AssertionError, msg):
|
||||
self.assertFormError(response, "form", "field", "invalid value")
|
||||
msg_prefix = "Custom prefix"
|
||||
with self.assertRaisesMessage(AssertionError, f"{msg_prefix}: {msg}"):
|
||||
self.assertFormError(
|
||||
response, "form", "field", "invalid value", msg_prefix=msg_prefix
|
||||
)
|
||||
|
||||
def test_single_error(self):
|
||||
self.assertFormError(TestForm.invalid(), "field", "invalid value")
|
||||
|
||||
|
@ -1523,35 +1485,6 @@ class AssertFormErrorTests(SimpleTestCase):
|
|||
|
||||
|
||||
class AssertFormSetErrorTests(SimpleTestCase):
|
||||
@ignore_warnings(category=RemovedInDjango50Warning)
|
||||
def test_non_client_response(self):
|
||||
msg = (
|
||||
"assertFormSetError() is only usable on responses fetched using "
|
||||
"the Django test Client."
|
||||
)
|
||||
response = HttpResponse()
|
||||
with self.assertRaisesMessage(ValueError, msg):
|
||||
self.assertFormSetError(response, "formset", 0, "field", "invalid value")
|
||||
|
||||
@ignore_warnings(category=RemovedInDjango50Warning)
|
||||
def test_response_with_no_context(self):
|
||||
msg = "Response did not use any contexts to render the response"
|
||||
response = mock.Mock(context=[])
|
||||
with self.assertRaisesMessage(AssertionError, msg):
|
||||
self.assertFormSetError(response, "formset", 0, "field", "invalid value")
|
||||
|
||||
@ignore_warnings(category=RemovedInDjango50Warning)
|
||||
def test_formset_not_in_context(self):
|
||||
msg = "The formset 'formset' was not used to render the response"
|
||||
response = mock.Mock(context=[{}])
|
||||
with self.assertRaisesMessage(AssertionError, msg):
|
||||
self.assertFormSetError(response, "formset", 0, "field", "invalid value")
|
||||
msg_prefix = "Custom prefix"
|
||||
with self.assertRaisesMessage(AssertionError, f"{msg_prefix}: {msg}"):
|
||||
self.assertFormSetError(
|
||||
response, "formset", 0, "field", "invalid value", msg_prefix=msg_prefix
|
||||
)
|
||||
|
||||
def test_rename_assertformseterror_deprecation_warning(self):
|
||||
msg = "assertFormsetError() is deprecated in favor of assertFormSetError()."
|
||||
with self.assertRaisesMessage(RemovedInDjango51Warning, msg):
|
||||
|
@ -1762,185 +1695,6 @@ class AssertFormSetErrorTests(SimpleTestCase):
|
|||
self.assertFormSetError(formset, 2, "field", "error")
|
||||
|
||||
|
||||
# RemovedInDjango50Warning
|
||||
class AssertFormErrorDeprecationTests(SimpleTestCase):
|
||||
"""
|
||||
Exhaustively test all possible combinations of args/kwargs for the old
|
||||
signature.
|
||||
"""
|
||||
|
||||
def _assert_form_error_old_api_cases(self, form, field, errors, msg_prefix):
|
||||
response = mock.Mock(context=[{"form": TestForm.invalid()}])
|
||||
return (
|
||||
((response, form, field, errors), {}),
|
||||
((response, form, field, errors, msg_prefix), {}),
|
||||
((response, form, field, errors), {"msg_prefix": msg_prefix}),
|
||||
((response, form, field), {"errors": errors}),
|
||||
((response, form, field), {"errors": errors, "msg_prefix": msg_prefix}),
|
||||
((response, form), {"field": field, "errors": errors}),
|
||||
(
|
||||
(response, form),
|
||||
{"field": field, "errors": errors, "msg_prefix": msg_prefix},
|
||||
),
|
||||
((response,), {"form": form, "field": field, "errors": errors}),
|
||||
(
|
||||
(response,),
|
||||
{
|
||||
"form": form,
|
||||
"field": field,
|
||||
"errors": errors,
|
||||
"msg_prefix": msg_prefix,
|
||||
},
|
||||
),
|
||||
(
|
||||
(),
|
||||
{"response": response, "form": form, "field": field, "errors": errors},
|
||||
),
|
||||
(
|
||||
(),
|
||||
{
|
||||
"response": response,
|
||||
"form": form,
|
||||
"field": field,
|
||||
"errors": errors,
|
||||
"msg_prefix": msg_prefix,
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
def test_assert_form_error_old_api(self):
|
||||
deprecation_msg = (
|
||||
"Passing response to assertFormError() is deprecated. Use the form object "
|
||||
"directly: assertFormError(response.context['form'], 'field', ...)"
|
||||
)
|
||||
for args, kwargs in self._assert_form_error_old_api_cases(
|
||||
form="form",
|
||||
field="field",
|
||||
errors=["invalid value"],
|
||||
msg_prefix="Custom prefix",
|
||||
):
|
||||
with self.subTest(args=args, kwargs=kwargs):
|
||||
with self.assertWarnsMessage(RemovedInDjango50Warning, deprecation_msg):
|
||||
self.assertFormError(*args, **kwargs)
|
||||
|
||||
@ignore_warnings(category=RemovedInDjango50Warning)
|
||||
def test_assert_form_error_old_api_assertion_error(self):
|
||||
for args, kwargs in self._assert_form_error_old_api_cases(
|
||||
form="form",
|
||||
field="field",
|
||||
errors=["other error"],
|
||||
msg_prefix="Custom prefix",
|
||||
):
|
||||
with self.subTest(args=args, kwargs=kwargs):
|
||||
with self.assertRaises(AssertionError):
|
||||
self.assertFormError(*args, **kwargs)
|
||||
|
||||
def _assert_formset_error_old_api_cases(
|
||||
self, formset, form_index, field, errors, msg_prefix
|
||||
):
|
||||
response = mock.Mock(context=[{"formset": TestFormset.invalid()}])
|
||||
return (
|
||||
((response, formset, form_index, field, errors), {}),
|
||||
((response, formset, form_index, field, errors, msg_prefix), {}),
|
||||
(
|
||||
(response, formset, form_index, field, errors),
|
||||
{"msg_prefix": msg_prefix},
|
||||
),
|
||||
((response, formset, form_index, field), {"errors": errors}),
|
||||
(
|
||||
(response, formset, form_index, field),
|
||||
{"errors": errors, "msg_prefix": msg_prefix},
|
||||
),
|
||||
((response, formset, form_index), {"field": field, "errors": errors}),
|
||||
(
|
||||
(response, formset, form_index),
|
||||
{"field": field, "errors": errors, "msg_prefix": msg_prefix},
|
||||
),
|
||||
(
|
||||
(response, formset),
|
||||
{"form_index": form_index, "field": field, "errors": errors},
|
||||
),
|
||||
(
|
||||
(response, formset),
|
||||
{
|
||||
"form_index": form_index,
|
||||
"field": field,
|
||||
"errors": errors,
|
||||
"msg_prefix": msg_prefix,
|
||||
},
|
||||
),
|
||||
(
|
||||
(response,),
|
||||
{
|
||||
"formset": formset,
|
||||
"form_index": form_index,
|
||||
"field": field,
|
||||
"errors": errors,
|
||||
},
|
||||
),
|
||||
(
|
||||
(response,),
|
||||
{
|
||||
"formset": formset,
|
||||
"form_index": form_index,
|
||||
"field": field,
|
||||
"errors": errors,
|
||||
"msg_prefix": msg_prefix,
|
||||
},
|
||||
),
|
||||
(
|
||||
(),
|
||||
{
|
||||
"response": response,
|
||||
"formset": formset,
|
||||
"form_index": form_index,
|
||||
"field": field,
|
||||
"errors": errors,
|
||||
},
|
||||
),
|
||||
(
|
||||
(),
|
||||
{
|
||||
"response": response,
|
||||
"formset": formset,
|
||||
"form_index": form_index,
|
||||
"field": field,
|
||||
"errors": errors,
|
||||
"msg_prefix": msg_prefix,
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
def test_assert_formset_error_old_api(self):
|
||||
deprecation_msg = (
|
||||
"Passing response to assertFormSetError() is deprecated. Use the formset "
|
||||
"object directly: assertFormSetError(response.context['formset'], 0, ...)"
|
||||
)
|
||||
for args, kwargs in self._assert_formset_error_old_api_cases(
|
||||
formset="formset",
|
||||
form_index=0,
|
||||
field="field",
|
||||
errors=["invalid value"],
|
||||
msg_prefix="Custom prefix",
|
||||
):
|
||||
with self.subTest(args=args, kwargs=kwargs):
|
||||
with self.assertWarnsMessage(RemovedInDjango50Warning, deprecation_msg):
|
||||
self.assertFormSetError(*args, **kwargs)
|
||||
|
||||
@ignore_warnings(category=RemovedInDjango50Warning)
|
||||
def test_assert_formset_error_old_api_assertion_error(self):
|
||||
for args, kwargs in self._assert_formset_error_old_api_cases(
|
||||
formset="formset",
|
||||
form_index=0,
|
||||
field="field",
|
||||
errors=["other error"],
|
||||
msg_prefix="Custom prefix",
|
||||
):
|
||||
with self.subTest(args=args, kwargs=kwargs):
|
||||
with self.assertRaises(AssertionError):
|
||||
self.assertFormSetError(*args, **kwargs)
|
||||
|
||||
|
||||
class FirstUrls:
|
||||
urlpatterns = [path("first/", empty_response, name="first")]
|
||||
|
||||
|
|
Loading…
Reference in New Issue