From 19403f22ca27f7717d123690a93e34a3d7b08d46 Mon Sep 17 00:00:00 2001 From: Charles-Meldhine Madi Mnemoi Date: Wed, 15 Oct 2025 14:51:36 +0200 Subject: [PATCH 1/4] fix(testing): `pytest.approx` returns a clearer error mesage when comparing mappings with different keys --- src/_pytest/python_api.py | 6 ++++++ testing/python/approx.py | 15 +++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index c64587bb219..1e389eb0663 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -242,6 +242,12 @@ def _repr_compare(self, other_side: Mapping[object, float]) -> list[str]: f"Lengths: {len(self.expected)} and {len(other_side)}", ] + if set(self.expected.keys()) != set(other_side.keys()): + return [ + "comparison failed.", + f"Mappings has different keys: expected {self.expected.keys()} but got {other_side.keys()}", + ] + approx_side_as_map = { k: self._approx_scalar(v) for k, v in self.expected.items() } diff --git a/testing/python/approx.py b/testing/python/approx.py index dc10a954839..651f9646307 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -9,6 +9,7 @@ import operator from operator import eq from operator import ne +import re from _pytest.pytester import Pytester from _pytest.python_api import _recursive_sequence_map @@ -1048,6 +1049,20 @@ def test_strange_sequence(self): assert b == pytest.approx(a, abs=2) assert b != pytest.approx(a, abs=0.5) + def test_approx_dicts_with_mismatch_on_keys(self) -> None: + """https://github.com/pytest-dev/pytest/issues/13816""" + expected = {"a": 1, "c": 3} + actual = {"a": 1, "b": 3} + + with pytest.raises( + AssertionError, + match=re.escape( + "comparison failed.\n Mappings has different keys: " + "expected dict_keys(['a', 'b']) but got dict_keys(['a', 'c'])" + ), + ): + assert expected == approx(actual) + class MyVec3: # incomplete """sequence like""" From 5696463523f141af8b5e3be390880bcb5ea6c9d7 Mon Sep 17 00:00:00 2001 From: Charles-Meldhine Madi Mnemoi Date: Wed, 15 Oct 2025 14:54:55 +0200 Subject: [PATCH 2/4] docs: add changelog entry --- changelog/13816.bugfix.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog/13816.bugfix.rst diff --git a/changelog/13816.bugfix.rst b/changelog/13816.bugfix.rst new file mode 100644 index 00000000000..432fc519ed5 --- /dev/null +++ b/changelog/13816.bugfix.rst @@ -0,0 +1 @@ +Fixed :func:`pytest.approx` which now returns a clearer error message when comparing mappings with different keys. From 8bcd02839a785578d17aefb025228443c07b4a35 Mon Sep 17 00:00:00 2001 From: Charles-Meldhine Madi Mnemoi Date: Wed, 15 Oct 2025 14:55:22 +0200 Subject: [PATCH 3/4] docs: add Charles-Meldhine Madi Mnemoi (cmnemoi) to AUTHORS --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index b28f49776d6..5849228200b 100644 --- a/AUTHORS +++ b/AUTHORS @@ -83,6 +83,7 @@ Carlos Jenkins Ceridwen Charles Cloud Charles Machalow +Charles-Meldhine Madi Mnemoi (cmnemoi) Charnjit SiNGH (CCSJ) Cheuk Ting Ho Chris Mahoney From 35dbdfc4006f51b1425cad6045652b9b5af219c2 Mon Sep 17 00:00:00 2001 From: Charles-Meldhine Madi Mnemoi Date: Wed, 15 Oct 2025 15:05:53 +0200 Subject: [PATCH 4/4] fix(testing): correct expected and actual dictionary keys --- testing/python/approx.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/testing/python/approx.py b/testing/python/approx.py index 651f9646307..0c14f6db1f3 100644 --- a/testing/python/approx.py +++ b/testing/python/approx.py @@ -1051,8 +1051,8 @@ def test_strange_sequence(self): def test_approx_dicts_with_mismatch_on_keys(self) -> None: """https://github.com/pytest-dev/pytest/issues/13816""" - expected = {"a": 1, "c": 3} - actual = {"a": 1, "b": 3} + expected = {"a": 1, "b": 3} + actual = {"a": 1, "c": 3} with pytest.raises( AssertionError, @@ -1061,7 +1061,7 @@ def test_approx_dicts_with_mismatch_on_keys(self) -> None: "expected dict_keys(['a', 'b']) but got dict_keys(['a', 'c'])" ), ): - assert expected == approx(actual) + assert actual == approx(expected) class MyVec3: # incomplete