From 46fe97a9c71bc3770fc0368138876819101e85de Mon Sep 17 00:00:00 2001 From: ENIO ROCHA Date: Thu, 16 Oct 2025 12:18:22 -0300 Subject: [PATCH 1/2] 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 --- src/_pytest/python_api.py | 11 ++++++ testing/test_approx_dict_keys_13816.py | 51 ++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 testing/test_approx_dict_keys_13816.py diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index c64587bb219..a7710f47bb0 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -242,6 +242,17 @@ def _repr_compare(self, other_side: Mapping[object, float]) -> list[str]: f"Lengths: {len(self.expected)} and {len(other_side)}", ] + # Check if keys match + expected_keys = set(self.expected.keys()) + other_keys = set(other_side.keys()) + if expected_keys != other_keys: + expected_only = expected_keys - other_keys + other_only = other_keys - expected_keys + msg = ["Dictionaries have different keys."] + if expected_only or other_only: + msg.append(f"Expected keys: {sorted(expected_keys)}, Actual keys: {sorted(other_keys)}") + return msg + approx_side_as_map = { k: self._approx_scalar(v) for k, v in self.expected.items() } diff --git a/testing/test_approx_dict_keys_13816.py b/testing/test_approx_dict_keys_13816.py new file mode 100644 index 00000000000..ad4c7ca3881 --- /dev/null +++ b/testing/test_approx_dict_keys_13816.py @@ -0,0 +1,51 @@ +"""Test for issue #13816 - better error messages for dict key mismatches in pytest.approx()""" +import pytest + + +def test_approx_dicts_with_different_keys_clear_error(): + """Test that pytest.approx() gives a clear error when dict keys don't match.""" + expected = {"a": 1, "c": 3} + actual = {"a": 1, "b": 4} + + with pytest.raises(AssertionError) as exc_info: + assert pytest.approx(actual) == expected + + error_message = str(exc_info.value) + # Should mention "different keys" clearly + assert "different keys" in error_message.lower() + # Should NOT have the confusing KeyError message + assert "KeyError" not in error_message + # Should NOT mention "faulty __repr__" + assert "faulty __repr__" not in error_message + + +def test_approx_dicts_with_extra_key_in_expected(): + """Test error message when expected has extra keys.""" + expected = {"a": 1, "b": 2, "c": 3} + actual = {"a": 1, "b": 2} + + with pytest.raises(AssertionError) as exc_info: + assert pytest.approx(actual) == expected + + error_message = str(exc_info.value) + assert "different keys" in error_message.lower() + + +def test_approx_dicts_with_extra_key_in_actual(): + """Test error message when actual has extra keys.""" + expected = {"a": 1, "b": 2} + actual = {"a": 1, "b": 2, "c": 3} + + with pytest.raises(AssertionError) as exc_info: + assert pytest.approx(actual) == expected + + error_message = str(exc_info.value) + assert "different keys" in error_message.lower() + + +def test_approx_dicts_matching_keys_still_works(): + """Test that dicts with matching keys still work normally.""" + expected = {"a": 1.0001, "b": 2.0001} + actual = {"a": 1.0, "b": 2.0} + + assert pytest.approx(actual, rel=1e-3) == expected From 039929076909ee09d615e2b4a0b23f0241219938 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 16 Oct 2025 15:19:33 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/_pytest/python_api.py | 4 +++- testing/test_approx_dict_keys_13816.py | 17 ++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/_pytest/python_api.py b/src/_pytest/python_api.py index a7710f47bb0..81799552f3f 100644 --- a/src/_pytest/python_api.py +++ b/src/_pytest/python_api.py @@ -250,7 +250,9 @@ def _repr_compare(self, other_side: Mapping[object, float]) -> list[str]: other_only = other_keys - expected_keys msg = ["Dictionaries have different keys."] if expected_only or other_only: - msg.append(f"Expected keys: {sorted(expected_keys)}, Actual keys: {sorted(other_keys)}") + msg.append( + f"Expected keys: {sorted(expected_keys)}, Actual keys: {sorted(other_keys)}" + ) return msg approx_side_as_map = { diff --git a/testing/test_approx_dict_keys_13816.py b/testing/test_approx_dict_keys_13816.py index ad4c7ca3881..e25433f8991 100644 --- a/testing/test_approx_dict_keys_13816.py +++ b/testing/test_approx_dict_keys_13816.py @@ -1,4 +1,7 @@ """Test for issue #13816 - better error messages for dict key mismatches in pytest.approx()""" + +from __future__ import annotations + import pytest @@ -6,10 +9,10 @@ def test_approx_dicts_with_different_keys_clear_error(): """Test that pytest.approx() gives a clear error when dict keys don't match.""" expected = {"a": 1, "c": 3} actual = {"a": 1, "b": 4} - + with pytest.raises(AssertionError) as exc_info: assert pytest.approx(actual) == expected - + error_message = str(exc_info.value) # Should mention "different keys" clearly assert "different keys" in error_message.lower() @@ -23,10 +26,10 @@ def test_approx_dicts_with_extra_key_in_expected(): """Test error message when expected has extra keys.""" expected = {"a": 1, "b": 2, "c": 3} actual = {"a": 1, "b": 2} - + with pytest.raises(AssertionError) as exc_info: assert pytest.approx(actual) == expected - + error_message = str(exc_info.value) assert "different keys" in error_message.lower() @@ -35,10 +38,10 @@ def test_approx_dicts_with_extra_key_in_actual(): """Test error message when actual has extra keys.""" expected = {"a": 1, "b": 2} actual = {"a": 1, "b": 2, "c": 3} - + with pytest.raises(AssertionError) as exc_info: assert pytest.approx(actual) == expected - + error_message = str(exc_info.value) assert "different keys" in error_message.lower() @@ -47,5 +50,5 @@ def test_approx_dicts_matching_keys_still_works(): """Test that dicts with matching keys still work normally.""" expected = {"a": 1.0001, "b": 2.0001} actual = {"a": 1.0, "b": 2.0} - + assert pytest.approx(actual, rel=1e-3) == expected