diff --git a/changelog/14461.bugfix.rst b/changelog/14461.bugfix.rst new file mode 100644 index 00000000000..01c5f2ec964 --- /dev/null +++ b/changelog/14461.bugfix.rst @@ -0,0 +1,2 @@ +Assertion diffs now use mapping-specific output for objects implementing +:class:`collections.abc.Mapping`, not only concrete :class:`dict` instances. diff --git a/src/_pytest/assertion/util.py b/src/_pytest/assertion/util.py index 07918a66284..d4ff86da52d 100644 --- a/src/_pytest/assertion/util.py +++ b/src/_pytest/assertion/util.py @@ -127,8 +127,8 @@ def istext(x: object) -> TypeGuard[str]: return isinstance(x, str) -def isdict(x: object) -> TypeGuard[dict[object, object]]: - return isinstance(x, dict) +def isdict(x: object) -> TypeGuard[Mapping[object, object]]: + return isinstance(x, Mapping) def isset(x: object) -> TypeGuard[set[object] | frozenset[object]]: diff --git a/testing/test_assertion.py b/testing/test_assertion.py index 9a7305a2905..4a487a0b7fe 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -1,6 +1,7 @@ # mypy: allow-untyped-defs from __future__ import annotations +from collections.abc import Mapping from collections.abc import MutableSequence import sys import textwrap @@ -719,6 +720,31 @@ def test_dict_wrap(self) -> None: " }", ] + def test_Mapping(self) -> None: + # Test comparing with a Mapping implementation that is not a dict subclass. + class TestMapping(Mapping[str, int]): + def __init__(self, values: dict[str, int]) -> None: + self.values = values + + def __getitem__(self, key: str) -> int: + return self.values[key] + + def __iter__(self): + return iter(self.values) + + def __len__(self) -> int: + return len(self.values) + + lines = callequal( + TestMapping({"a": 0, "b": 1}), + TestMapping({"a": 1, "b": 1}), + ) + + assert lines is not None + assert any(line.startswith("Omitting 1 identical item") for line in lines) + assert "Differing items:" in lines + assert "{'a': 0} != {'a': 1}" in lines + def test_dict(self) -> None: expl = callequal({"a": 0}, {"a": 1}) assert expl is not None