From 13b200ab615a7f6d49cd35c5a831622ae79a2998 Mon Sep 17 00:00:00 2001 From: Andy Venet Date: Sat, 29 Aug 2026 20:23:48 -0300 Subject: [PATCH 1/6] Add configuration for Django assertion diff length --- docs/changelog.rst | 6 ++++++ docs/configuring_django.rst | 18 ++++++++++++++++++ pytest_django/plugin.py | 18 ++++++++++++++++++ tests/test_asserts.py | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index e1c8a982..84b1fb90 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -9,6 +9,12 @@ Compatibility * Official Django 6.1 support. +Improvements +^^^^^^^^^^^^ + +* Added the ``django_asserts_max_diff`` configuration option to set the + maximum diff length emitted by ``pytest_django.asserts`` (`#1155 `__). + v4.14.0 (2026-08-10) -------------------- diff --git a/docs/configuring_django.rst b/docs/configuring_django.rst index 75f247e2..d238cbf8 100644 --- a/docs/configuring_django.rst +++ b/docs/configuring_django.rst @@ -59,6 +59,24 @@ The order of precedence is, from highest to lowest: If you want to use the highest precedence in the configuration file, you can use ``addopts = --ds=yourtestsettings``. +Configuring Django assertion diffs +---------------------------------- + +The assertion helpers in :mod:`pytest_django.asserts` use Django's +``TestCase`` assertions. To change the maximum amount of assertion diff shown, +set ``django_asserts_max_diff`` in your pytest configuration. The default is +``640``, matching ``unittest.TestCase.maxDiff``. Set it to ``None`` to show the +complete diff:: + + [pytest] + django_asserts_max_diff = None + +In ``pyproject.toml``, use a string because TOML does not have a ``None`` +literal:: + + [tool.pytest.ini_options] + django_asserts_max_diff = "None" + Using django-configurations --------------------------- diff --git a/pytest_django/plugin.py b/pytest_django/plugin.py index aa45ff38..79d8936f 100644 --- a/pytest_django/plugin.py +++ b/pytest_django/plugin.py @@ -142,6 +142,11 @@ def pytest_addoption(parser: pytest.Parser) -> None: "How to set the Django DEBUG setting (default `False`). Use `keep` to not override.", default="False", ) + parser.addini( + "django_asserts_max_diff", + "Maximum diff length for pytest_django.asserts (use `None` for no limit).", + default="640", + ) group.addoption( "--fail-on-template-vars", action="store_true", @@ -410,6 +415,19 @@ def pytest_configure(config: pytest.Config) -> None: # it's fully initialized here. _setup_django(config) + from pytest_django.asserts import test_case + + max_diff = config.getini("django_asserts_max_diff") + if max_diff.lower() == "none": + test_case.maxDiff = None + else: + try: + test_case.maxDiff = int(max_diff) + except ValueError as error: + raise pytest.UsageError( + "django_asserts_max_diff must be an integer or None" + ) from error + @pytest.hookimpl() def pytest_report_header(config: pytest.Config) -> list[str] | None: diff --git a/tests/test_asserts.py b/tests/test_asserts.py index 7dbbf710..4b56bd1d 100644 --- a/tests/test_asserts.py +++ b/tests/test_asserts.py @@ -69,3 +69,35 @@ def test_sanity() -> None: pass assert assertContains.__doc__ + + +def test_django_asserts_max_diff_can_be_unlimited( + django_pytester: pytest.Pytester, +) -> None: + django_pytester.makeini( + """ + [pytest] + django_asserts_max_diff = None + """ + ) + django_pytester.makepyfile( + """ + from pytest_django.asserts import assertXMLEqual + + + def test_assert_xml_equal_uses_the_configured_max_diff(): + expected = "" + "".join(f"{i}" for i in range(100)) + "" + actual = "" + "".join(f"{i + 1}" for i in range(100)) + "" + + try: + assertXMLEqual(expected, actual) + except AssertionError as error: + assert "Set self.maxDiff to None" not in str(error) + else: + raise AssertionError("assertXMLEqual unexpectedly passed") + """ + ) + + result = django_pytester.runpytest_subprocess() + + result.assert_outcomes(passed=1) From d9b05d980ad62e94c8535b33ba4e3a0d660ef900 Mon Sep 17 00:00:00 2001 From: Andy Venet Date: Sat, 29 Aug 2026 20:35:13 -0300 Subject: [PATCH 2/6] Avoid loading Django for assertion diff config --- pytest_django/plugin.py | 3 +++ tests/test_without_django_loaded.py | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/pytest_django/plugin.py b/pytest_django/plugin.py index 79d8936f..68510123 100644 --- a/pytest_django/plugin.py +++ b/pytest_django/plugin.py @@ -415,6 +415,9 @@ def pytest_configure(config: pytest.Config) -> None: # it's fully initialized here. _setup_django(config) + if "django" not in sys.modules: + return + from pytest_django.asserts import test_case max_diff = config.getini("django_asserts_max_diff") diff --git a/tests/test_without_django_loaded.py b/tests/test_without_django_loaded.py index cd376fa6..7e93ca45 100644 --- a/tests/test_without_django_loaded.py +++ b/tests/test_without_django_loaded.py @@ -26,6 +26,30 @@ def test_cfg(pytestconfig): assert r.ret == 0 +def test_django_asserts_max_diff_does_not_import_django( + pytester: pytest.Pytester, +) -> None: + pytester.makeini( + """ + [pytest] + django_asserts_max_diff = None + """ + ) + pytester.makepyfile( + """ + import sys + + + def test_django_is_not_imported(): + assert "django" not in sys.modules + """ + ) + + result = pytester.runpytest_subprocess() + + result.assert_outcomes(passed=1) + + def test_database(pytester: pytest.Pytester) -> None: pytester.makepyfile( """ From c5a7c845911b83a5486a7638577dbeebb3f05252 Mon Sep 17 00:00:00 2001 From: Andy Venet Date: Sat, 29 Aug 2026 20:40:01 -0300 Subject: [PATCH 3/6] Fix typing and docs for assertion diff config --- docs/configuring_django.rst | 2 +- pytest_django/asserts.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/configuring_django.rst b/docs/configuring_django.rst index d238cbf8..4c508a5b 100644 --- a/docs/configuring_django.rst +++ b/docs/configuring_django.rst @@ -62,7 +62,7 @@ use ``addopts = --ds=yourtestsettings``. Configuring Django assertion diffs ---------------------------------- -The assertion helpers in :mod:`pytest_django.asserts` use Django's +The assertion helpers in ``pytest_django.asserts`` use Django's ``TestCase`` assertions. To change the maximum amount of assertion diff shown, set ``django_asserts_max_diff`` in your pytest configuration. The default is ``640``, matching ``unittest.TestCase.maxDiff``. Set it to ``None`` to show the diff --git a/pytest_django/asserts.py b/pytest_django/asserts.py index 47d636d5..9bffc618 100644 --- a/pytest_django/asserts.py +++ b/pytest_django/asserts.py @@ -16,7 +16,7 @@ class MessagesTestCase(MessagesTestMixin, TestCase): pass -test_case = MessagesTestCase("run") +test_case: Any = MessagesTestCase("run") def _wrapper(name: str) -> Callable[..., Any]: From 830514be258e225a139f40cac8165df834d7a15c Mon Sep 17 00:00:00 2001 From: Andy Venet Date: Sat, 29 Aug 2026 20:41:53 -0300 Subject: [PATCH 4/6] Hide dynamic assertion case from plugin typing --- pytest_django/asserts.py | 4 ++++ pytest_django/plugin.py | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pytest_django/asserts.py b/pytest_django/asserts.py index 9bffc618..d9130354 100644 --- a/pytest_django/asserts.py +++ b/pytest_django/asserts.py @@ -19,6 +19,10 @@ class MessagesTestCase(MessagesTestMixin, TestCase): test_case: Any = MessagesTestCase("run") +def _set_max_diff(max_diff: int | None) -> None: + test_case.maxDiff = max_diff + + def _wrapper(name: str) -> Callable[..., Any]: func = getattr(test_case, name) diff --git a/pytest_django/plugin.py b/pytest_django/plugin.py index 68510123..f405988d 100644 --- a/pytest_django/plugin.py +++ b/pytest_django/plugin.py @@ -418,14 +418,14 @@ def pytest_configure(config: pytest.Config) -> None: if "django" not in sys.modules: return - from pytest_django.asserts import test_case + from pytest_django.asserts import _set_max_diff max_diff = config.getini("django_asserts_max_diff") if max_diff.lower() == "none": - test_case.maxDiff = None + _set_max_diff(None) else: try: - test_case.maxDiff = int(max_diff) + _set_max_diff(int(max_diff)) except ValueError as error: raise pytest.UsageError( "django_asserts_max_diff must be an integer or None" From da2ff6ef5816326bb7d86453dbf5c84fef41d13a Mon Sep 17 00:00:00 2001 From: Andy Venet Date: Sat, 29 Aug 2026 21:02:04 -0300 Subject: [PATCH 5/6] Test invalid assertion diff configuration --- tests/test_asserts.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/test_asserts.py b/tests/test_asserts.py index 4b56bd1d..8cd23528 100644 --- a/tests/test_asserts.py +++ b/tests/test_asserts.py @@ -101,3 +101,21 @@ def test_assert_xml_equal_uses_the_configured_max_diff(): result = django_pytester.runpytest_subprocess() result.assert_outcomes(passed=1) + + +def test_django_asserts_max_diff_requires_an_integer_or_none( + django_pytester: pytest.Pytester, +) -> None: + django_pytester.makeini( + """ + [pytest] + django_asserts_max_diff = unlimited + """ + ) + + result = django_pytester.runpytest_subprocess() + + assert result.ret == pytest.ExitCode.USAGE_ERROR + result.stderr.fnmatch_lines( + ["*django_asserts_max_diff must be an integer or None*"] + ) From 70da1106aa1590d047d0e889191f2c6936b5ff46 Mon Sep 17 00:00:00 2001 From: Andy Venet Date: Sat, 29 Aug 2026 21:04:17 -0300 Subject: [PATCH 6/6] Format assertion diff configuration changes --- tests/test_asserts.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_asserts.py b/tests/test_asserts.py index 8cd23528..c59578a6 100644 --- a/tests/test_asserts.py +++ b/tests/test_asserts.py @@ -116,6 +116,4 @@ def test_django_asserts_max_diff_requires_an_integer_or_none( result = django_pytester.runpytest_subprocess() assert result.ret == pytest.ExitCode.USAGE_ERROR - result.stderr.fnmatch_lines( - ["*django_asserts_max_diff must be an integer or None*"] - ) + result.stderr.fnmatch_lines(["*django_asserts_max_diff must be an integer or None*"])