Skip to content

Commit e95a843

Browse files
authored
Fix pytest.approx to correctly take account Mapping keys order to compare them (#13815)
This fixes an issue with `pytest.approx` where the error message incorrectly reported all elements as mismatched when comparing mappings with different key orders, even when only some values differed. The original code paired values by position rather than by key. This caused incorrect mismatch reporting when dictionary keys were in different orders. Fixes #12444
1 parent 6687d95 commit e95a843

File tree

3 files changed

+44
-4
lines changed

3 files changed

+44
-4
lines changed

changelog/12444.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fixed :func:`pytest.approx` which now correctly takes account Mapping keys order to compare them.

src/_pytest/python_api.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ def _repr_compare(self, other_side: Mapping[object, float]) -> list[str]:
242242
f"Lengths: {len(self.expected)} and {len(other_side)}",
243243
]
244244

245-
if set(self.expected.keys()) != set(other_side.keys()):
245+
if self.expected.keys() != other_side.keys():
246246
return [
247247
"comparison failed.",
248248
f"Mappings has different keys: expected {self.expected.keys()} but got {other_side.keys()}",
@@ -256,9 +256,8 @@ def _repr_compare(self, other_side: Mapping[object, float]) -> list[str]:
256256
max_abs_diff = -math.inf
257257
max_rel_diff = -math.inf
258258
different_ids = []
259-
for (approx_key, approx_value), other_value in zip(
260-
approx_side_as_map.items(), other_side.values(), strict=True
261-
):
259+
for approx_key, approx_value in approx_side_as_map.items():
260+
other_value = other_side[approx_key]
262261
if approx_value != other_value:
263262
if approx_value.expected is not None and other_value is not None:
264263
try:

testing/python/approx.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,6 +1062,46 @@ def test_approx_dicts_with_mismatch_on_keys(self) -> None:
10621062
):
10631063
assert actual == approx(expected)
10641064

1065+
def test_approx_on_unordered_mapping_with_mismatch(
1066+
self, pytester: Pytester
1067+
) -> None:
1068+
"""https://github.com/pytest-dev/pytest/issues/12444"""
1069+
pytester.makepyfile(
1070+
"""
1071+
import pytest
1072+
1073+
def test_approx_on_unordered_mapping_with_mismatch():
1074+
expected = {"a": 1, "b": 2, "c": 3, "d": 4}
1075+
actual = {"d": 4, "c": 5, "a": 8, "b": 2}
1076+
assert actual == pytest.approx(expected)
1077+
"""
1078+
)
1079+
result = pytester.runpytest()
1080+
result.assert_outcomes(failed=1)
1081+
result.stdout.fnmatch_lines(
1082+
[
1083+
"*comparison failed.**Mismatched elements: 2 / 4:*",
1084+
"*Max absolute difference: 7*",
1085+
"*Index | Obtained | Expected *",
1086+
"* a * | 8 * | 1 *",
1087+
"* c * | 5 * | 3 *",
1088+
]
1089+
)
1090+
1091+
def test_approx_on_unordered_mapping_matching(self, pytester: Pytester) -> None:
1092+
"""https://github.com/pytest-dev/pytest/issues/12444"""
1093+
pytester.makepyfile(
1094+
"""
1095+
import pytest
1096+
def test_approx_on_unordered_mapping_matching():
1097+
expected = {"a": 1, "b": 2, "c": 3, "d": 4}
1098+
actual = {"d": 4, "c": 3, "a": 1, "b": 2}
1099+
assert actual == pytest.approx(expected)
1100+
"""
1101+
)
1102+
result = pytester.runpytest()
1103+
result.assert_outcomes(passed=1)
1104+
10651105

10661106
class MyVec3: # incomplete
10671107
"""sequence like"""

0 commit comments

Comments
 (0)