diff --git a/django/test/testcases.py b/django/test/testcases.py index b0fd5e89a1..871f141725 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -54,7 +54,7 @@ from django.test.utils import ( modify_settings, override_settings, ) -from django.utils.deprecation import RemovedInDjango50Warning +from django.utils.deprecation import RemovedInDjango50Warning, RemovedInDjango51Warning from django.utils.functional import classproperty from django.utils.version import PY310 from django.views.static import serve @@ -206,18 +206,18 @@ class _AssertFormErrorDeprecationHelper: ) @staticmethod - def assertFormsetError( + 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 + 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"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) @@ -234,7 +234,7 @@ class _AssertFormErrorDeprecationHelper: if formset not in context or not hasattr(context[formset], "forms"): continue found_formset = True - self.assertFormsetError( + self.assertFormSetError( context[formset], form_index, field, errors, msg_prefix ) if not found_formset: @@ -737,10 +737,19 @@ class SimpleTestCase(unittest.TestCase): errors = to_list(errors) self._assert_form_error(form, field, errors, msg_prefix, f"form {form!r}") + # RemovedInDjango51Warning. + def assertFormsetError(self, *args, **kw): + warnings.warn( + "assertFormsetError() is deprecated in favor of assertFormSetError().", + category=RemovedInDjango51Warning, + stacklevel=2, + ) + 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=""): + def assertFormSetError(self, formset, form_index, field, errors, msg_prefix=""): """ Similar to assertFormError() but for formsets. @@ -752,7 +761,7 @@ class SimpleTestCase(unittest.TestCase): """ if errors is None: warnings.warn( - "Passing errors=None to assertFormsetError() is deprecated, " + "Passing errors=None to assertFormSetError() is deprecated, " "use errors=[] instead.", RemovedInDjango50Warning, stacklevel=2, diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index a5b76c13ab..cbde99fb06 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -35,6 +35,8 @@ details on these changes. * The ``map_width`` and ``map_height`` attributes of ``BaseGeometryWidget`` will be removed. +* The ``SimpleTestCase.assertFormsetError()`` method will be removed. + .. _deprecation-removed-in-5.0: 5.0 diff --git a/docs/releases/1.6.txt b/docs/releases/1.6.txt index 4109565448..2d77c7dc85 100644 --- a/docs/releases/1.6.txt +++ b/docs/releases/1.6.txt @@ -321,7 +321,7 @@ Minor features * The :class:`~django.test.SimpleTestCase` class includes a new assertion helper for testing formset errors: - :meth:`~django.test.SimpleTestCase.assertFormsetError`. + ``django.test.SimpleTestCase.assertFormsetError()``. * The list of related fields added to a :class:`~django.db.models.query.QuerySet` by diff --git a/docs/releases/4.0.1.txt b/docs/releases/4.0.1.txt index d5e129a153..dba3f1783f 100644 --- a/docs/releases/4.0.1.txt +++ b/docs/releases/4.0.1.txt @@ -50,8 +50,7 @@ Bugfixes ======== * Fixed a regression in Django 4.0 that caused a crash of - :meth:`~django.test.SimpleTestCase.assertFormsetError` on a formset named - ``form`` (:ticket:`33346`). + ``assertFormsetError()`` on a formset named ``form`` (:ticket:`33346`). * Fixed a bug in Django 4.0 that caused a crash on booleans with the ``RedisCache`` backend (:ticket:`33361`). diff --git a/docs/releases/4.1.txt b/docs/releases/4.1.txt index 08fb7c2abc..55e19e09ea 100644 --- a/docs/releases/4.1.txt +++ b/docs/releases/4.1.txt @@ -407,8 +407,8 @@ Tests raises a ``RuntimeError``, the same as outside of tests. * :meth:`.SimpleTestCase.assertFormError` and - :meth:`~.SimpleTestCase.assertFormsetError` now support passing a - form/formset object directly. + :meth:`assertFormsetError() ` + now support passing a form/formset object directly. URLs ~~~~ @@ -671,8 +671,8 @@ Miscellaneous * The undocumented ability to pass ``errors=None`` to :meth:`.SimpleTestCase.assertFormError` and - :meth:`~.SimpleTestCase.assertFormsetError` is deprecated. Use ``errors=[]`` - instead. + :meth:`assertFormsetError() ` + is deprecated. Use ``errors=[]`` instead. * ``django.contrib.sessions.serializers.PickleSerializer`` is deprecated due to the risk of remote code execution. diff --git a/docs/releases/4.2.txt b/docs/releases/4.2.txt index 5774bfef7b..33035d7881 100644 --- a/docs/releases/4.2.txt +++ b/docs/releases/4.2.txt @@ -419,3 +419,6 @@ Miscellaneous * The ``map_height`` and ``map_width`` attributes of ``BaseGeometryWidget`` are deprecated, use CSS to size map widgets instead. + +* ``SimpleTestCase.assertFormsetError()`` is deprecated in favor of + ``assertFormSetError()``. diff --git a/docs/topics/testing/tools.txt b/docs/topics/testing/tools.txt index 1f235d920b..4e2003acde 100644 --- a/docs/topics/testing/tools.txt +++ b/docs/topics/testing/tools.txt @@ -1573,16 +1573,16 @@ your test suite. ``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='') +.. method:: SimpleTestCase.assertFormSetError(formset, form_index, field, errors, msg_prefix='') Asserts that the ``formset`` raises the provided list of errors when rendered. - ``formset`` is a ``Formset`` instance. The formset must be bound but not - necessarily validated (``assertFormsetError()`` will automatically call the + ``formset`` is a ``FormSet`` instance. The formset must be bound but not + necessarily validated (``assertFormSetError()`` will automatically call the ``full_clean()`` on the formset). - ``form_index`` is the number of the form within the ``Formset`` (starting + ``form_index`` is the number of the form within the ``FormSet`` (starting from 0). Use ``form_index=None`` to check the formset's non-form errors, i.e. the errors you get when calling ``formset.non_form_errors()``. In that case you must also use ``field=None``. @@ -1593,9 +1593,14 @@ your test suite. .. deprecated:: 4.1 Support for passing a response object and a formset name to - ``assertFormsetError()`` is deprecated and will be removed in Django + ``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 + ``assertFormSetError()`` instead. + .. method:: SimpleTestCase.assertContains(response, text, count=None, status_code=200, msg_prefix='', html=False) Asserts that a :class:`response ` produced the diff --git a/tests/admin_views/tests.py b/tests/admin_views/tests.py index cd5b8ed836..9796703ad1 100644 --- a/tests/admin_views/tests.py +++ b/tests/admin_views/tests.py @@ -8110,7 +8110,7 @@ class AdminViewOnSiteTests(TestCase): Issue #20522 Verifying that if the parent form fails validation, the inlines also run validation even if validation is contingent on parent form data. - Also, assertFormError() and assertFormsetError() is usable for admin + Also, assertFormError() and assertFormSetError() is usable for admin forms and formsets. """ # The form validation should fail because 'some_required_info' is @@ -8134,7 +8134,7 @@ class AdminViewOnSiteTests(TestCase): ["This field is required."], ) self.assertFormError(response.context["adminform"], None, []) - self.assertFormsetError( + self.assertFormSetError( response.context["inline_admin_formset"], 0, None, @@ -8143,7 +8143,7 @@ class AdminViewOnSiteTests(TestCase): "contrived test case" ], ) - self.assertFormsetError( + self.assertFormSetError( response.context["inline_admin_formset"], None, None, [] ) @@ -8179,7 +8179,7 @@ class AdminViewOnSiteTests(TestCase): "some_required_info", ["This field is required."], ) - self.assertFormsetError( + self.assertFormSetError( response.context["inline_admin_formset"], 0, None, diff --git a/tests/test_utils/tests.py b/tests/test_utils/tests.py index 709060d76f..b22aa273d5 100644 --- a/tests/test_utils/tests.py +++ b/tests/test_utils/tests.py @@ -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 +from django.utils.deprecation import RemovedInDjango50Warning, RemovedInDjango51Warning from django.utils.log import DEFAULT_LOGGING from django.utils.version import PY311 @@ -1517,44 +1517,53 @@ class AssertFormErrorTests(SimpleTestCase): ) -class AssertFormsetErrorTests(SimpleTestCase): +class AssertFormSetErrorTests(SimpleTestCase): @ignore_warnings(category=RemovedInDjango50Warning) def test_non_client_response(self): msg = ( - "assertFormsetError() is only usable on responses fetched using " + "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") + 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") + 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") + self.assertFormSetError(response, "formset", 0, "field", "invalid value") msg_prefix = "Custom prefix" with self.assertRaisesMessage(AssertionError, f"{msg_prefix}: {msg}"): - self.assertFormsetError( + self.assertFormSetError( response, "formset", 0, "field", "invalid value", msg_prefix=msg_prefix ) - def test_single_error(self): + def test_rename_assertformseterror_deprecation_warning(self): + msg = "assertFormsetError() is deprecated in favor of assertFormSetError()." + with self.assertRaisesMessage(RemovedInDjango51Warning, msg): + self.assertFormsetError() + + @ignore_warnings(category=RemovedInDjango51Warning) + def test_deprecated_assertformseterror(self): self.assertFormsetError(TestFormset.invalid(), 0, "field", "invalid value") + def test_single_error(self): + self.assertFormSetError(TestFormset.invalid(), 0, "field", "invalid value") + def test_error_list(self): - self.assertFormsetError(TestFormset.invalid(), 0, "field", ["invalid value"]) + self.assertFormSetError(TestFormset.invalid(), 0, "field", ["invalid value"]) def test_empty_errors_valid_formset(self): - self.assertFormsetError(TestFormset.valid(), 0, "field", []) + self.assertFormSetError(TestFormset.valid(), 0, "field", []) def test_multiple_forms(self): formset = TestFormset( @@ -1566,8 +1575,8 @@ class AssertFormsetErrorTests(SimpleTestCase): } ) formset.full_clean() - self.assertFormsetError(formset, 0, "field", []) - self.assertFormsetError(formset, 1, "field", ["invalid value"]) + self.assertFormSetError(formset, 0, "field", []) + self.assertFormSetError(formset, 1, "field", ["invalid value"]) def test_field_not_in_form(self): msg = ( @@ -1575,12 +1584,12 @@ class AssertFormsetErrorTests(SimpleTestCase): "does not contain the field 'other_field'." ) with self.assertRaisesMessage(AssertionError, msg): - self.assertFormsetError( + self.assertFormSetError( TestFormset.invalid(), 0, "other_field", "invalid value" ) msg_prefix = "Custom prefix" with self.assertRaisesMessage(AssertionError, f"{msg_prefix}: {msg}"): - self.assertFormsetError( + self.assertFormSetError( TestFormset.invalid(), 0, "other_field", @@ -1594,11 +1603,11 @@ class AssertFormsetErrorTests(SimpleTestCase): "valid=True total_forms=1> don't match." ) with self.assertRaisesMessage(AssertionError, msg) as ctx: - self.assertFormsetError(TestFormset.valid(), 0, "field", "invalid value") + self.assertFormSetError(TestFormset.valid(), 0, "field", "invalid value") self.assertIn("[] != ['invalid value']", str(ctx.exception)) msg_prefix = "Custom prefix" with self.assertRaisesMessage(AssertionError, f"{msg_prefix}: {msg}"): - self.assertFormsetError( + self.assertFormSetError( TestFormset.valid(), 0, "field", "invalid value", msg_prefix=msg_prefix ) @@ -1608,11 +1617,11 @@ class AssertFormsetErrorTests(SimpleTestCase): "valid=False total_forms=1> don't match." ) with self.assertRaisesMessage(AssertionError, msg) as ctx: - self.assertFormsetError(TestFormset.invalid(), 0, "field", "other error") + self.assertFormSetError(TestFormset.invalid(), 0, "field", "other error") self.assertIn("['invalid value'] != ['other error']", str(ctx.exception)) msg_prefix = "Custom prefix" with self.assertRaisesMessage(AssertionError, f"{msg_prefix}: {msg}"): - self.assertFormsetError( + self.assertFormSetError( TestFormset.invalid(), 0, "field", "other error", msg_prefix=msg_prefix ) @@ -1622,7 +1631,7 @@ class AssertFormsetErrorTests(SimpleTestCase): "bound, it will never have any errors." ) with self.assertRaisesMessage(AssertionError, msg): - self.assertFormsetError(TestFormset(), 0, "field", []) + self.assertFormSetError(TestFormset(), 0, "field", []) def test_empty_errors_invalid_formset(self): msg = ( @@ -1630,11 +1639,11 @@ class AssertFormsetErrorTests(SimpleTestCase): "valid=False total_forms=1> don't match." ) with self.assertRaisesMessage(AssertionError, msg) as ctx: - self.assertFormsetError(TestFormset.invalid(), 0, "field", []) + self.assertFormSetError(TestFormset.invalid(), 0, "field", []) self.assertIn("['invalid value'] != []", str(ctx.exception)) def test_non_field_errors(self): - self.assertFormsetError( + self.assertFormSetError( TestFormset.invalid(nonfield=True), 0, None, "non-field error" ) @@ -1644,7 +1653,7 @@ class AssertFormsetErrorTests(SimpleTestCase): "valid=False total_forms=1> don't match." ) with self.assertRaisesMessage(AssertionError, msg) as ctx: - self.assertFormsetError( + self.assertFormSetError( TestFormset.invalid(nonfield=True), 0, None, "other non-field error" ) self.assertIn( @@ -1652,7 +1661,7 @@ class AssertFormsetErrorTests(SimpleTestCase): ) msg_prefix = "Custom prefix" with self.assertRaisesMessage(AssertionError, f"{msg_prefix}: {msg}"): - self.assertFormsetError( + self.assertFormSetError( TestFormset.invalid(nonfield=True), 0, None, @@ -1666,16 +1675,16 @@ class AssertFormsetErrorTests(SimpleTestCase): "valid=False total_forms=1> don't match." ) with self.assertRaisesMessage(AssertionError, msg) as ctx: - self.assertFormsetError(TestFormset.invalid(), 0, None, "non-field error") + self.assertFormSetError(TestFormset.invalid(), 0, None, "non-field error") self.assertIn("[] != ['non-field error']", str(ctx.exception)) msg_prefix = "Custom prefix" with self.assertRaisesMessage(AssertionError, f"{msg_prefix}: {msg}"): - self.assertFormsetError( + self.assertFormSetError( TestFormset.invalid(), 0, None, "non-field error", msg_prefix=msg_prefix ) def test_non_form_errors(self): - self.assertFormsetError(TestFormset.invalid(nonform=True), None, None, "error") + self.assertFormSetError(TestFormset.invalid(nonform=True), None, None, "error") def test_different_non_form_errors(self): msg = ( @@ -1683,13 +1692,13 @@ class AssertFormsetErrorTests(SimpleTestCase): "total_forms=0> don't match." ) with self.assertRaisesMessage(AssertionError, msg) as ctx: - self.assertFormsetError( + self.assertFormSetError( TestFormset.invalid(nonform=True), None, None, "other error" ) self.assertIn("['error'] != ['other error']", str(ctx.exception)) msg_prefix = "Custom prefix" with self.assertRaisesMessage(AssertionError, f"{msg_prefix}: {msg}"): - self.assertFormsetError( + self.assertFormSetError( TestFormset.invalid(nonform=True), None, None, @@ -1703,11 +1712,11 @@ class AssertFormsetErrorTests(SimpleTestCase): "total_forms=1> don't match." ) with self.assertRaisesMessage(AssertionError, msg) as ctx: - self.assertFormsetError(TestFormset.invalid(), None, None, "error") + self.assertFormSetError(TestFormset.invalid(), None, None, "error") self.assertIn("[] != ['error']", str(ctx.exception)) msg_prefix = "Custom prefix" with self.assertRaisesMessage(AssertionError, f"{msg_prefix}: {msg}"): - self.assertFormsetError( + self.assertFormSetError( TestFormset.invalid(), None, None, @@ -1718,7 +1727,7 @@ class AssertFormsetErrorTests(SimpleTestCase): def test_non_form_errors_with_field(self): msg = "You must use field=None with form_index=None." with self.assertRaisesMessage(ValueError, msg): - self.assertFormsetError( + self.assertFormSetError( TestFormset.invalid(nonform=True), None, "field", "error" ) @@ -1728,7 +1737,7 @@ class AssertFormsetErrorTests(SimpleTestCase): "1 form." ) with self.assertRaisesMessage(AssertionError, msg): - self.assertFormsetError(TestFormset.invalid(), 2, "field", "error") + self.assertFormSetError(TestFormset.invalid(), 2, "field", "error") def test_form_index_too_big_plural(self): formset = TestFormset( @@ -1745,7 +1754,7 @@ class AssertFormsetErrorTests(SimpleTestCase): "forms." ) with self.assertRaisesMessage(AssertionError, msg): - self.assertFormsetError(formset, 2, "field", "error") + self.assertFormSetError(formset, 2, "field", "error") # RemovedInDjango50Warning @@ -1845,15 +1854,15 @@ class AssertFormErrorDeprecationTests(SimpleTestCase): "valid=False total_forms=1> don't match." ) with self.assertRaisesMessage(AssertionError, msg): - self.assertFormsetError(TestFormset.invalid(), 0, "field", None) + self.assertFormSetError(TestFormset.invalid(), 0, "field", None) def test_assert_formset_error_errors_none_warning(self): msg = ( - "Passing errors=None to assertFormsetError() is deprecated, use " + "Passing errors=None to assertFormSetError() is deprecated, use " "errors=[] instead." ) with self.assertWarnsMessage(RemovedInDjango50Warning, msg): - self.assertFormsetError(TestFormset.valid(), 0, "field", None) + self.assertFormSetError(TestFormset.valid(), 0, "field", None) def _assert_formset_error_old_api_cases( self, formset, form_index, field, errors, msg_prefix @@ -1933,8 +1942,8 @@ class AssertFormErrorDeprecationTests(SimpleTestCase): 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, ...)" + "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", @@ -1945,7 +1954,7 @@ class AssertFormErrorDeprecationTests(SimpleTestCase): ): with self.subTest(args=args, kwargs=kwargs): with self.assertWarnsMessage(RemovedInDjango50Warning, deprecation_msg): - self.assertFormsetError(*args, **kwargs) + self.assertFormSetError(*args, **kwargs) @ignore_warnings(category=RemovedInDjango50Warning) def test_assert_formset_error_old_api_assertion_error(self): @@ -1958,7 +1967,7 @@ class AssertFormErrorDeprecationTests(SimpleTestCase): ): with self.subTest(args=args, kwargs=kwargs): with self.assertRaises(AssertionError): - self.assertFormsetError(*args, **kwargs) + self.assertFormSetError(*args, **kwargs) class FirstUrls: