Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed #33301 -- Clarified the type of arguments required by custom assertions. #15106

Merged
merged 3 commits into from Nov 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
28 changes: 19 additions & 9 deletions django/test/testcases.py
Expand Up @@ -477,11 +477,23 @@ def assertNotContains(self, response, text, status_code=200, msg_prefix='', html

self.assertEqual(real_count, 0, msg_prefix + "Response should not contain %s" % text_repr)

def _check_test_client_response(self, response, attribute, method_name):
"""
Raise a ValueError if the given response doesn't have the required
attribute.
"""
if not hasattr(response, attribute):
raise ValueError(
f"{method_name}() is only usable on responses fetched using "
"the Django test Client."
)

def assertFormError(self, response, form, field, errors, msg_prefix=''):
"""
Assert that a form used to render the response has a specific field
error.
"""
self._check_test_client_response(response, 'context', 'assertFormError')
if msg_prefix:
msg_prefix += ": "

Expand Down Expand Up @@ -543,6 +555,7 @@ def assertFormsetError(self, response, formset, form_index, field, errors,
For non-form errors, specify ``form_index`` as None and the ``field``
as None.
"""
self._check_test_client_response(response, 'context', 'assertFormsetError')
# Add punctuation to msg_prefix
if msg_prefix:
msg_prefix += ": "
Expand Down Expand Up @@ -612,19 +625,16 @@ def assertFormsetError(self, response, formset, form_index, field, errors,
if not found_formset:
self.fail(msg_prefix + "The formset '%s' was not used to render the response" % formset)

def _assert_template_used(self, response, template_name, msg_prefix):
def _assert_template_used(self, response, template_name, msg_prefix, method_name):

if response is None and template_name is None:
raise TypeError('response and/or template_name argument must be provided')

if msg_prefix:
msg_prefix += ": "

if template_name is not None and response is not None and not hasattr(response, 'templates'):
raise ValueError(
"assertTemplateUsed() and assertTemplateNotUsed() are only "
"usable on responses fetched using the Django test Client."
)
if template_name is not None and response is not None:
self._check_test_client_response(response, 'templates', method_name)

if not hasattr(response, 'templates') or (response is None and template_name):
if response:
Expand All @@ -642,8 +652,8 @@ def assertTemplateUsed(self, response=None, template_name=None, msg_prefix='', c
the response. Also usable as context manager.
"""
context_mgr_template, template_names, msg_prefix = self._assert_template_used(
response, template_name, msg_prefix)

response, template_name, msg_prefix, 'assertTemplateUsed',
)
if context_mgr_template:
# Use assertTemplateUsed as context manager.
return _AssertTemplateUsedContext(self, context_mgr_template)
Expand Down Expand Up @@ -671,7 +681,7 @@ def assertTemplateNotUsed(self, response=None, template_name=None, msg_prefix=''
rendering the response. Also usable as context manager.
"""
context_mgr_template, template_names, msg_prefix = self._assert_template_used(
response, template_name, msg_prefix
response, template_name, msg_prefix, 'assertTemplateNotUsed',
)
if context_mgr_template:
# Use assertTemplateNotUsed as context manager.
Expand Down
36 changes: 24 additions & 12 deletions docs/topics/testing/tools.txt
Expand Up @@ -1478,8 +1478,11 @@ your test suite.
Asserts that a field on a form raises the provided list of errors when
rendered on the form.

``response`` must be a response instance returned by the
:class:`test client <django.test.Response>`.

``form`` is the name the ``Form`` instance was given in the template
context.
context of the response.

``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
Expand All @@ -1494,8 +1497,11 @@ your test suite.
Asserts that the ``formset`` raises the provided list of errors when
rendered.

``response`` must be a response instance returned by the
:class:`test client <django.test.Response>`.

``formset`` is the name the ``Formset`` instance was given in the template
context.
context of the response.

``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
Expand All @@ -1511,9 +1517,10 @@ your test suite.

.. 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
that ``text`` appears in the content of the response. If ``count`` is
provided, ``text`` must occur exactly ``count`` times in the response.
Asserts that a :class:`response <django.http.HttpResponse>` produced the
given :attr:`~django.http.HttpResponse.status_code` and that ``text``
appears in its :attr:`~django.http.HttpResponse.content`. If ``count``
is provided, ``text`` must occur exactly ``count`` times in the response.

Set ``html`` to ``True`` to handle ``text`` as HTML. The comparison with
the response content will be based on HTML semantics instead of
Expand All @@ -1523,8 +1530,9 @@ your test suite.

.. method:: SimpleTestCase.assertNotContains(response, text, status_code=200, msg_prefix='', html=False)

Asserts that a ``Response`` instance produced the given ``status_code`` and
that ``text`` does *not* appear in the content of the response.
Asserts that a :class:`response <django.http.HttpResponse>` produced the
given :attr:`~django.http.HttpResponse.status_code` and that ``text`` does
*not* appear in its :attr:`~django.http.HttpResponse.content`.

Set ``html`` to ``True`` to handle ``text`` as HTML. The comparison with
the response content will be based on HTML semantics instead of
Expand All @@ -1537,9 +1545,12 @@ your test suite.
Asserts that the template with the given name was used in rendering the
response.

The name is a string such as ``'admin/index.html'``.
``response`` must be a response instance returned by the
:class:`test client <django.test.Response>`.

``template_name`` should be a string such as ``'admin/index.html'``.

The count argument is an integer indicating the number of times the
The ``count`` argument is an integer indicating the number of times the
template should be rendered. Default is ``None``, meaning that the template
should be rendered one or more times.

Expand Down Expand Up @@ -1567,9 +1578,10 @@ your test suite.

.. method:: SimpleTestCase.assertRedirects(response, expected_url, status_code=302, target_status_code=200, msg_prefix='', fetch_redirect_response=True)

Asserts that the response returned a ``status_code`` redirect status,
redirected to ``expected_url`` (including any ``GET`` data), and that the
final page was received with ``target_status_code``.
Asserts that the :class:`response <django.http.HttpResponse>` returned a
:attr:`~django.http.HttpResponse.status_code` redirect status, redirected
to ``expected_url`` (including any ``GET`` data), and that the final page
was received with ``target_status_code``.

If your request used the ``follow`` argument, the ``expected_url`` and
``target_status_code`` will be the url and status code for the final
Expand Down