=============================
Django assert element
=============================
.. image:: https://badge.fury.io/py/assert_element.svg
:target: https://badge.fury.io/py/assert_element
.. image:: https://codecov.io/gh/PetrDlouhy/assert_element/branch/master/graph/badge.svg
:target: https://codecov.io/gh/PetrDlouhy/assert_element
.. image:: https://github.com/PetrDlouhy/django-assert-element/actions/workflows/main.yml/badge.svg?event=registry_package
:target: https://github.com/PetrDlouhy/django-assert-element/actions/workflows/main.yml
Simple ``TestCase`` assertion that finds element based on it's xpath and check if it equals with given content.
In case the content is not matching it outputs nice and clean diff of the two compared HTML pieces.
This is more useful than the default Django ``self.assertContains(response, ..., html=True)``
because it will find the element and show differences if something changed.
Whitespace Normalization
~~~~~~~~~~~~~~~~~~~~~~~~~
The library uses aggressive whitespace normalization to focus on HTML semantic meaning
rather than cosmetic formatting differences:
* **Normalizes cosmetic differences**: Multiple spaces, tabs, newlines, and attribute spacing
* **Handles structural variations**: Self-closing vs explicit tags (``<br/>`` vs ``<br></br>``)
* **Preserves semantic meaning**: Only fails when HTML content actually differs in meaning
* **Browser-consistent**: Mimics how browsers treat whitespace (collapsed to single spaces)
This prevents false positive test failures caused by insignificant whitespace variations
while still catching genuine HTML content differences.
Other similar projects
----------------------
I released this package just to realize after few days, that there are some other very similar projects:
* https://pypi.org/project/django_html_assertions/
* https://django-with-asserts.readthedocs.io/en/latest/
* https://github.com/robjohncox/python-html-assert
Documentation
-------------
The full documentation is at https://assert_element.readthedocs.io.
Quickstart
----------
Install by:
.. code-block:: bash
pip install assert-element
Usage in tests:
.. code-block:: python
from assert_element import AssertElementMixin
class MyTestCase(AssertElementMixin, TestCase):
def test_something(self):
response = self.client.get(address)
self.assertElementContains(
response,
'div[id="my-div"]',
'<div id="my-div">My div</div>',
)
The first attribute can be response or content string.
Second attribute is the xpath to the element.
Third attribute is the expected content.
**Error Output Example**: If response = `<html><div id="my-div">Myy div</div></html>` the error output of the `assertElementContains` looks like this:
.. code-block:: console
======================================================================
FAIL: test_element_differs (tests.test_models.MyTestCase.test_element_differs)
Element not found raises Exception
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/petr/soubory/programovani/blenderkit/django-assert-element/assert_element/tests/test_models.py", line 53, in test_element_differs
self.assertElementContains(
File "/home/petr/soubory/programovani/blenderkit/django-assert-element/assert_element/assert_element/assert_element.py", line 58, in assertElementContains
self.assertEqual(element_txt, soup_1_txt)
AssertionError: '<div\n id="my-div"\n>\n Myy div \n</div>' != '<div\n id="my-div"\n>\n My div \n</div>'
<div
id="my-div"
>
- Myy div
? -
+ My div
</div>
which is much cleaner than the original django ``assertContains()`` output.
**Whitespace Example**: These assertions would pass because the differences are cosmetic:
.. code-block:: python
# These are all equivalent due to whitespace normalization:
self.assertElementContains(response, 'p', '<p>hello world</p>')
self.assertElementContains(response, 'p', '<p>hello world</p>') # Multiple spaces
self.assertElementContains(response, 'p', '<p>hello\tworld</p>') # Tab
self.assertElementContains(response, 'p', '<p>\n hello world \n</p>') # Newlines
Running Tests
-------------
Does the code actually work?
::
source <YOURVIRTUALENV>/bin/activate
(myenv) $ pip install tox
(myenv) $ tox
Development commands
---------------------
::
pip install -r requirements_dev.txt
invoke -l
Credits
-------
Tools used in rendering this package:
* Cookiecutter_
* `cookiecutter-djangopackage`_
.. _Cookiecutter: https://github.com/audreyr/cookiecutter
.. _`cookiecutter-djangopackage`: https://github.com/pydanny/cookiecutter-djangopackage
History
-------
0.5.0 (2025-08-15)
++++++++++++++++++
* improved whitespace sanitization with aggressive normalization
* enhanced test coverage for semantically meaningful whitespace differences
* updated documentation with detailed whitespace normalization behavior
0.4.0 (2023-07-21)
++++++++++++++++++
* more readable output when assertion fails
0.3.0 (2022-09-16)
++++++++++++++++++
* more tolerance in whitespace differences
0.2.0 (2022-09-01)
++++++++++++++++++
* first attribute can be response or content itself
0.1.0 (2022-08-21)
++++++++++++++++++
* First release on PyPI.
Raw data
{
"_id": null,
"home_page": "https://github.com/PetrDlouhy/django-assert-element",
"name": "assert-element",
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": null,
"keywords": "assert_element",
"author": "Petr Dlouh\u00fd",
"author_email": "petr.dlouhy@email.cz",
"download_url": "https://files.pythonhosted.org/packages/6b/89/3b18d0400e6ee8cb779bfbda25ea83741dd75d2723b0be2d562072144a13/assert_element-0.5.0.tar.gz",
"platform": null,
"description": "=============================\nDjango assert element\n=============================\n\n.. image:: https://badge.fury.io/py/assert_element.svg\n :target: https://badge.fury.io/py/assert_element\n\n.. image:: https://codecov.io/gh/PetrDlouhy/assert_element/branch/master/graph/badge.svg\n :target: https://codecov.io/gh/PetrDlouhy/assert_element\n\n.. image:: https://github.com/PetrDlouhy/django-assert-element/actions/workflows/main.yml/badge.svg?event=registry_package\n :target: https://github.com/PetrDlouhy/django-assert-element/actions/workflows/main.yml\n\nSimple ``TestCase`` assertion that finds element based on it's xpath and check if it equals with given content.\nIn case the content is not matching it outputs nice and clean diff of the two compared HTML pieces.\n\nThis is more useful than the default Django ``self.assertContains(response, ..., html=True)``\nbecause it will find the element and show differences if something changed.\n\nWhitespace Normalization\n~~~~~~~~~~~~~~~~~~~~~~~~~\n\nThe library uses aggressive whitespace normalization to focus on HTML semantic meaning\nrather than cosmetic formatting differences:\n\n* **Normalizes cosmetic differences**: Multiple spaces, tabs, newlines, and attribute spacing\n* **Handles structural variations**: Self-closing vs explicit tags (``<br/>`` vs ``<br></br>``)\n* **Preserves semantic meaning**: Only fails when HTML content actually differs in meaning\n* **Browser-consistent**: Mimics how browsers treat whitespace (collapsed to single spaces)\n\nThis prevents false positive test failures caused by insignificant whitespace variations\nwhile still catching genuine HTML content differences.\n\nOther similar projects\n----------------------\n\nI released this package just to realize after few days, that there are some other very similar projects:\n\n* https://pypi.org/project/django_html_assertions/\n* https://django-with-asserts.readthedocs.io/en/latest/\n* https://github.com/robjohncox/python-html-assert\n\nDocumentation\n-------------\n\nThe full documentation is at https://assert_element.readthedocs.io.\n\nQuickstart\n----------\n\nInstall by:\n\n.. code-block:: bash\n \n pip install assert-element\n\nUsage in tests:\n\n.. code-block:: python\n\n from assert_element import AssertElementMixin\n\n class MyTestCase(AssertElementMixin, TestCase):\n def test_something(self):\n response = self.client.get(address)\n self.assertElementContains(\n response,\n 'div[id=\"my-div\"]',\n '<div id=\"my-div\">My div</div>',\n )\n\nThe first attribute can be response or content string.\nSecond attribute is the xpath to the element.\nThird attribute is the expected content.\n\n**Error Output Example**: If response = `<html><div id=\"my-div\">Myy div</div></html>` the error output of the `assertElementContains` looks like this:\n\n.. code-block:: console\n\n ======================================================================\n FAIL: test_element_differs (tests.test_models.MyTestCase.test_element_differs)\n Element not found raises Exception\n ----------------------------------------------------------------------\n Traceback (most recent call last):\n File \"/home/petr/soubory/programovani/blenderkit/django-assert-element/assert_element/tests/test_models.py\", line 53, in test_element_differs\n self.assertElementContains(\n File \"/home/petr/soubory/programovani/blenderkit/django-assert-element/assert_element/assert_element/assert_element.py\", line 58, in assertElementContains\n self.assertEqual(element_txt, soup_1_txt)\n AssertionError: '<div\\n id=\"my-div\"\\n>\\n Myy div \\n</div>' != '<div\\n id=\"my-div\"\\n>\\n My div \\n</div>'\n <div\n id=\"my-div\"\n >\n - Myy div \n ? -\n + My div \n </div>\n\nwhich is much cleaner than the original django ``assertContains()`` output.\n\n**Whitespace Example**: These assertions would pass because the differences are cosmetic:\n\n.. code-block:: python\n\n # These are all equivalent due to whitespace normalization:\n self.assertElementContains(response, 'p', '<p>hello world</p>')\n self.assertElementContains(response, 'p', '<p>hello world</p>') # Multiple spaces\n self.assertElementContains(response, 'p', '<p>hello\\tworld</p>') # Tab\n self.assertElementContains(response, 'p', '<p>\\n hello world \\n</p>') # Newlines\n\nRunning Tests\n-------------\n\nDoes the code actually work?\n\n::\n\n source <YOURVIRTUALENV>/bin/activate\n (myenv) $ pip install tox\n (myenv) $ tox\n\n\nDevelopment commands\n---------------------\n\n::\n\n pip install -r requirements_dev.txt\n invoke -l\n\n\nCredits\n-------\n\nTools used in rendering this package:\n\n* Cookiecutter_\n* `cookiecutter-djangopackage`_\n\n.. _Cookiecutter: https://github.com/audreyr/cookiecutter\n.. _`cookiecutter-djangopackage`: https://github.com/pydanny/cookiecutter-djangopackage\n\n\n\n\nHistory\n-------\n\n0.5.0 (2025-08-15)\n++++++++++++++++++\n\n* improved whitespace sanitization with aggressive normalization\n* enhanced test coverage for semantically meaningful whitespace differences\n* updated documentation with detailed whitespace normalization behavior\n\n0.4.0 (2023-07-21)\n++++++++++++++++++\n\n* more readable output when assertion fails\n\n0.3.0 (2022-09-16)\n++++++++++++++++++\n\n* more tolerance in whitespace differences\n\n0.2.0 (2022-09-01)\n++++++++++++++++++\n\n* first attribute can be response or content itself\n\n0.1.0 (2022-08-21)\n++++++++++++++++++\n\n* First release on PyPI.\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Simple TestCase assertion that finds element based on it's path and check if it equals with given content.",
"version": "0.5.0",
"project_urls": {
"Homepage": "https://github.com/PetrDlouhy/django-assert-element"
},
"split_keywords": [
"assert_element"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "73c57f26943b2510ff77a8956e2796346b02cf9a2256e82ef0bb6398071e9471",
"md5": "c51e20c95e6550055bc7949cba6db458",
"sha256": "cc5a738b2e55c0e08390a20554d77d95aa07d67a047a4ceff56493e49be60ecc"
},
"downloads": -1,
"filename": "assert_element-0.5.0-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "c51e20c95e6550055bc7949cba6db458",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": null,
"size": 5993,
"upload_time": "2025-08-15T08:42:47",
"upload_time_iso_8601": "2025-08-15T08:42:47.051143Z",
"url": "https://files.pythonhosted.org/packages/73/c5/7f26943b2510ff77a8956e2796346b02cf9a2256e82ef0bb6398071e9471/assert_element-0.5.0-py2.py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "6b893b18d0400e6ee8cb779bfbda25ea83741dd75d2723b0be2d562072144a13",
"md5": "f8431018a2409a93e02ff6f71199fbd6",
"sha256": "c0ee9330c0b2ced9c3323496027099e4c507bb1af12a453fdc554765add03191"
},
"downloads": -1,
"filename": "assert_element-0.5.0.tar.gz",
"has_sig": false,
"md5_digest": "f8431018a2409a93e02ff6f71199fbd6",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 23264,
"upload_time": "2025-08-15T08:42:48",
"upload_time_iso_8601": "2025-08-15T08:42:48.041944Z",
"url": "https://files.pythonhosted.org/packages/6b/89/3b18d0400e6ee8cb779bfbda25ea83741dd75d2723b0be2d562072144a13/assert_element-0.5.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-15 08:42:48",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "PetrDlouhy",
"github_project": "django-assert-element",
"travis_ci": false,
"coveralls": true,
"github_actions": true,
"requirements": [
{
"name": "beautifulsoup4",
"specs": []
}
],
"tox": true,
"lcname": "assert-element"
}