Skip to content

Commit 46fe97a

Browse files
committed
fix: improve error message when pytest.approx() dicts have different keys (#13816)
- Added clear check for key mismatch before value comparison - Now reports "Dictionaries have different keys" instead of confusing KeyError - Shows which keys are in expected vs actual - Added 4 focused test cases Before: AssertionError: ... (pytest_assertion plugin: representation of details failed: KeyError: 'b'. Probably an object has a faulty __repr__.) After: AssertionError: Dictionaries have different keys. Expected keys: ['a', 'c'], Actual keys: ['a', 'b'] Tests: - test_approx_dicts_with_different_keys_clear_error: PASS - test_approx_dicts_with_extra_key_in_expected: PASS - test_approx_dicts_with_extra_key_in_actual: PASS - test_approx_dicts_matching_keys_still_works: PASS Fixes #13816 Sacred Code: 000.111.369.963.1618
1 parent c97a401 commit 46fe97a

File tree

2 files changed

+62
-0
lines changed

2 files changed

+62
-0
lines changed

src/_pytest/python_api.py

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

245+
# Check if keys match
246+
expected_keys = set(self.expected.keys())
247+
other_keys = set(other_side.keys())
248+
if expected_keys != other_keys:
249+
expected_only = expected_keys - other_keys
250+
other_only = other_keys - expected_keys
251+
msg = ["Dictionaries have different keys."]
252+
if expected_only or other_only:
253+
msg.append(f"Expected keys: {sorted(expected_keys)}, Actual keys: {sorted(other_keys)}")
254+
return msg
255+
245256
approx_side_as_map = {
246257
k: self._approx_scalar(v) for k, v in self.expected.items()
247258
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""Test for issue #13816 - better error messages for dict key mismatches in pytest.approx()"""
2+
import pytest
3+
4+
5+
def test_approx_dicts_with_different_keys_clear_error():
6+
"""Test that pytest.approx() gives a clear error when dict keys don't match."""
7+
expected = {"a": 1, "c": 3}
8+
actual = {"a": 1, "b": 4}
9+
10+
with pytest.raises(AssertionError) as exc_info:
11+
assert pytest.approx(actual) == expected
12+
13+
error_message = str(exc_info.value)
14+
# Should mention "different keys" clearly
15+
assert "different keys" in error_message.lower()
16+
# Should NOT have the confusing KeyError message
17+
assert "KeyError" not in error_message
18+
# Should NOT mention "faulty __repr__"
19+
assert "faulty __repr__" not in error_message
20+
21+
22+
def test_approx_dicts_with_extra_key_in_expected():
23+
"""Test error message when expected has extra keys."""
24+
expected = {"a": 1, "b": 2, "c": 3}
25+
actual = {"a": 1, "b": 2}
26+
27+
with pytest.raises(AssertionError) as exc_info:
28+
assert pytest.approx(actual) == expected
29+
30+
error_message = str(exc_info.value)
31+
assert "different keys" in error_message.lower()
32+
33+
34+
def test_approx_dicts_with_extra_key_in_actual():
35+
"""Test error message when actual has extra keys."""
36+
expected = {"a": 1, "b": 2}
37+
actual = {"a": 1, "b": 2, "c": 3}
38+
39+
with pytest.raises(AssertionError) as exc_info:
40+
assert pytest.approx(actual) == expected
41+
42+
error_message = str(exc_info.value)
43+
assert "different keys" in error_message.lower()
44+
45+
46+
def test_approx_dicts_matching_keys_still_works():
47+
"""Test that dicts with matching keys still work normally."""
48+
expected = {"a": 1.0001, "b": 2.0001}
49+
actual = {"a": 1.0, "b": 2.0}
50+
51+
assert pytest.approx(actual, rel=1e-3) == expected

0 commit comments

Comments
 (0)