From 2bf46c3825ad4ec170324791d6f3a329316ae2d4 Mon Sep 17 00:00:00 2001 From: Nicolas Lupien Date: Wed, 20 Dec 2023 11:49:47 -0500 Subject: [PATCH] Fixed #34658 -- Added SimpleTestCase.assertNotInHTML(). --- django/test/testcases.py | 22 ++++++++++++++-------- docs/releases/5.1.txt | 3 +++ docs/topics/testing/tools.txt | 10 ++++++++++ tests/test_utils/tests.py | 10 ++++++++++ 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/django/test/testcases.py b/django/test/testcases.py index 0e887bdc0b..b5d426f75f 100644 --- a/django/test/testcases.py +++ b/django/test/testcases.py @@ -922,14 +922,17 @@ class SimpleTestCase(unittest.TestCase): msg_prefix += ": " haystack_repr = safe_repr(haystack) if count is not None: - self.assertEqual( - real_count, - count, - ( - f"{msg_prefix}Found {real_count} instances of {needle!r} (expected " - f"{count}) in the following response\n{haystack_repr}" - ), - ) + if count == 0: + msg = ( + f"{needle!r} unexpectedly found in the following response\n" + f"{haystack_repr}" + ) + else: + msg = ( + f"Found {real_count} instances of {needle!r} (expected {count}) in " + f"the following response\n{haystack_repr}" + ) + self.assertEqual(real_count, count, f"{msg_prefix}{msg}") else: self.assertTrue( real_count != 0, @@ -939,6 +942,9 @@ class SimpleTestCase(unittest.TestCase): ), ) + def assertNotInHTML(self, needle, haystack, msg_prefix=""): + self.assertInHTML(needle, haystack, count=0, msg_prefix=msg_prefix) + def assertJSONEqual(self, raw, expected_data, msg=None): """ Assert that the JSON fragments raw and expected_data are equal. diff --git a/docs/releases/5.1.txt b/docs/releases/5.1.txt index fffaec21c6..cc72346eef 100644 --- a/docs/releases/5.1.txt +++ b/docs/releases/5.1.txt @@ -238,6 +238,9 @@ Tests self.client.post("/items/1", query_params={"action": "delete"}) await self.async_client.post("/items/1", query_params={"action": "delete"}) +* The new :meth:`.SimpleTestCase.assertNotInHTML` assertion allows testing that + an HTML fragment is not contained in the given HTML haystack. + URLs ~~~~ diff --git a/docs/topics/testing/tools.txt b/docs/topics/testing/tools.txt index b01dd35b8c..068e452ad0 100644 --- a/docs/topics/testing/tools.txt +++ b/docs/topics/testing/tools.txt @@ -1905,6 +1905,16 @@ your test suite. In older versions, error messages didn't contain the ``haystack``. +.. method:: SimpleTestCase.assertNotInHTML(needle, haystack, msg_prefix="") + + .. versionadded:: 5.1 + + Asserts that the HTML fragment ``needle`` is *not* contained in the + ``haystack``. + + Whitespace in most cases is ignored, and attribute ordering is not + significant. See :meth:`~SimpleTestCase.assertHTMLEqual` for more details. + .. method:: SimpleTestCase.assertJSONEqual(raw, expected_data, msg=None) Asserts that the JSON fragments ``raw`` and ``expected_data`` are equal. diff --git a/tests/test_utils/tests.py b/tests/test_utils/tests.py index bce060f7ef..ce78ffc008 100644 --- a/tests/test_utils/tests.py +++ b/tests/test_utils/tests.py @@ -1053,6 +1053,16 @@ class InHTMLTests(SimpleTestCase): with self.assertRaisesMessage(AssertionError, msg): self.assertInHTML("This", haystack, 3) + def test_assert_not_in_html(self): + haystack = "

Hello there! Hi there!

" + self.assertNotInHTML("Hi", haystack=haystack) + msg = ( + "'Hello' unexpectedly found in the following response" + f"\n{haystack!r}" + ) + with self.assertRaisesMessage(AssertionError, msg): + self.assertNotInHTML("Hello", haystack=haystack) + class JSONEqualTests(SimpleTestCase): def test_simple_equal(self):