From c67e1cf44f17c36139e25b1eae92216cb8baad77 Mon Sep 17 00:00:00 2001 From: Baptiste Mispelon Date: Tue, 4 Jan 2022 13:02:14 +0100 Subject: [PATCH] Refs #33348 -- Deprecated passing errors=None to SimpleTestCase.assertFormError()/assertFormsetErrors(). --- django/test/testcases.py | 31 ++++++++++++++++++++++--------- docs/internals/deprecation.txt | 4 ++++ docs/releases/4.1.txt | 5 +++++ tests/test_utils/tests.py | 31 ++++++++++++++++++++++++++++++- 4 files changed, 61 insertions(+), 10 deletions(-) diff --git a/django/test/testcases.py b/django/test/testcases.py index 9759680f0f..1344d550a9 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -6,6 +6,7 @@ import posixpath import sys import threading import unittest +import warnings from collections import Counter from contextlib import contextmanager from copy import copy, deepcopy @@ -41,6 +42,7 @@ from django.test.utils import ( CaptureQueriesContext, ContextList, compare_xml, modify_settings, override_settings, ) +from django.utils.deprecation import RemovedInDjango50Warning from django.utils.functional import classproperty from django.utils.version import PY310 from django.views.static import serve @@ -50,13 +52,8 @@ __all__ = ('TestCase', 'TransactionTestCase', def to_list(value): - """ - Put value into a list if it's not already one. Return an empty list if - value is None. - """ - if value is None: - value = [] - elif not isinstance(value, list): + """Put value into a list if it's not already one.""" + if not isinstance(value, list): value = [value] return value @@ -493,10 +490,18 @@ class SimpleTestCase(unittest.TestCase): msg_prefix += ": " # Put context(s) into a list to simplify processing. - contexts = to_list(response.context) + contexts = [] if response.context is None else to_list(response.context) if not contexts: self.fail(msg_prefix + "Response did not use any contexts to render the response") + if errors is None: + warnings.warn( + 'Passing errors=None to assertFormError() is deprecated, use ' + 'errors=[] instead.', + RemovedInDjango50Warning, + stacklevel=2, + ) + errors = [] # Put error(s) into a list to simplify processing. errors = to_list(errors) @@ -556,11 +561,19 @@ class SimpleTestCase(unittest.TestCase): msg_prefix += ": " # Put context(s) into a list to simplify processing. - contexts = to_list(response.context) + contexts = [] if response.context is None else to_list(response.context) if not contexts: self.fail(msg_prefix + 'Response did not use any contexts to ' 'render the response') + if errors is None: + warnings.warn( + 'Passing errors=None to assertFormsetError() is deprecated, ' + 'use errors=[] instead.', + RemovedInDjango50Warning, + stacklevel=2, + ) + errors = [] # Put error(s) into a list to simplify processing. errors = to_list(errors) diff --git a/docs/internals/deprecation.txt b/docs/internals/deprecation.txt index 939bc5049e..4ab7a4135d 100644 --- a/docs/internals/deprecation.txt +++ b/docs/internals/deprecation.txt @@ -75,6 +75,10 @@ details on these changes. * The ``opclasses`` argument of ``django.contrib.postgres.constraints.ExclusionConstraint`` will be removed. +* The undocumented ability to pass ``errors=None`` to + ``SimpleTestCase.assertFormError()`` and ``assertFormsetError()`` will be + removed. + .. _deprecation-removed-in-4.1: 4.1 diff --git a/docs/releases/4.1.txt b/docs/releases/4.1.txt index 11d2c5ca04..219201fd00 100644 --- a/docs/releases/4.1.txt +++ b/docs/releases/4.1.txt @@ -385,6 +385,11 @@ Miscellaneous ), ] +* The undocumented ability to pass ``errors=None`` to + :meth:`.SimpleTestCase.assertFormError` and + :meth:`~.SimpleTestCase.assertFormsetError` is deprecated. Use ``errors=[]`` + instead. + Features removed in 4.1 ======================= diff --git a/tests/test_utils/tests.py b/tests/test_utils/tests.py index 3380a1c67e..9474373138 100644 --- a/tests/test_utils/tests.py +++ b/tests/test_utils/tests.py @@ -26,10 +26,11 @@ from django.test import ( from django.test.html import HTMLParseError, parse_html from django.test.testcases import DatabaseOperationForbidden from django.test.utils import ( - CaptureQueriesContext, TestContextDecorator, isolate_apps, + CaptureQueriesContext, TestContextDecorator, ignore_warnings, isolate_apps, override_settings, setup_test_environment, ) from django.urls import NoReverseMatch, path, reverse, reverse_lazy +from django.utils.deprecation import RemovedInDjango50Warning from django.utils.log import DEFAULT_LOGGING from .models import Car, Person, PossessedCar @@ -1418,6 +1419,20 @@ class AssertFormErrorTests(SimpleTestCase): response = mock.Mock(context=[{'form': TestForm.invalid(nonfield=True)}]) self.assertFormError(response, 'form', None, 'non-field error') + @ignore_warnings(category=RemovedInDjango50Warning) + def test_errors_none(self): + response = mock.Mock(context=[{'form': TestForm.invalid()}]) + self.assertFormError(response, 'form', 'field', None) + + def test_errors_none_warning(self): + response = mock.Mock(context=[{'form': TestForm.invalid()}]) + msg = ( + 'Passing errors=None to assertFormError() is deprecated, use ' + 'errors=[] instead.' + ) + with self.assertWarnsMessage(RemovedInDjango50Warning, msg): + self.assertFormError(response, 'form', 'value', None) + class AssertFormsetErrorTests(SimpleTestCase): def _get_formset_data(self, field_value): @@ -1560,6 +1575,20 @@ class AssertFormsetErrorTests(SimpleTestCase): ]) self.assertFormsetError(response, 'form', 0, 'field', 'invalid value') + @ignore_warnings(category=RemovedInDjango50Warning) + def test_errors_none(self): + response = mock.Mock(context=[{'formset': TestFormset.invalid()}]) + self.assertFormsetError(response, 'formset', 0, 'field', None) + + def test_errors_none_warning(self): + response = mock.Mock(context=[{'formset': TestFormset.invalid()}]) + msg = ( + 'Passing errors=None to assertFormsetError() is deprecated, use ' + 'errors=[] instead.' + ) + with self.assertWarnsMessage(RemovedInDjango50Warning, msg): + self.assertFormsetError(response, 'formset', 0, 'field', None) + class FirstUrls: urlpatterns = [path('first/', empty_response, name='first')]