Reputation: 1962
I am attempting to write a nested pytest approx class to compare complex structures while allowing floats to be compared with approximation.
import pytest
from _pytest.python_api import ApproxMapping
from collections.abc import Mapping
def nested_approx(expected, rel=None, abs=None, nan_ok=False):
if isinstance(expected, Mapping):
return ApproxNestedMapping(expected, rel, abs, nan_ok)
else:
return pytest.approx(expected, rel=rel, abs=abs, nan_ok=nan_ok)
class ApproxNestedMapping(ApproxMapping):
def _yield_comparisons(self, actual):
if not isinstance(actual, Mapping):
raise TypeError(f"Expected a mapping but got {type(actual)}")
# Ensure all keys match exactly
expected_keys = set(self.expected.keys())
actual_keys = set(actual.keys())
if expected_keys != actual_keys:
missing = expected_keys - actual_keys
extra = actual_keys - expected_keys
raise AssertionError(f"Key mismatch:\n Missing: {missing}\n Extra: {extra}")
# Compare values, ensuring key order doesn't affect comparison
for k in sorted(expected_keys): # Sorting ensures stable comparisons
yield actual[k], nested_approx(
self.expected[k],
rel=self.rel,
abs=self.abs,
nan_ok=self.nan_ok
)
def _check_type(self):
if not isinstance(self.expected, Mapping):
raise TypeError(f"Expected a mapping but got {type(self.expected)}")
def test_redux_this_fails():
actual = {
'trail_angle': 10.593548226756798,
'joint': { },
}
expected = {
"joint": { },
"trail_angle": 10.463173570164972,
}
assert actual == nested_approx(expected)
The result of this pytest is this:
_______________________________ test_redux_this_fails ________________________________
def test_redux_this_fails():
actual = {
'trail_angle': 10.593548226756798,
'joint': { },
}
expected = {
"joint": { },
"trail_angle": 10.463173570164972,
}
> assert actual == nested_approx(expected)
E AssertionError: assert {'trail_angle': 10.593548226756798, 'joint': {}} == approx({'joint': {}, 'trail_angle': 10.463173570164972 ± 1.0e-05})
E
E (pytest_assertion plugin: representation of details failed: /home/mmachenry/src/keystone/ScheduleServer/services/rollingmodel/.tox/py39/lib/python3.9/site-packages/_pytest/python_api.py:267: TypeError: unsupported operand type(s) for -: 'dict' and 'float'.
E Probably an object has a faulty __repr__.)
This unit test works just fine when the floating point numbers are the same number
def test_redux_this_passes():
actual = {
'trail_angle': 10.593548226756798,
'joint': { },
}
expected = {
"joint": { },
'trail_angle': 10.593548226756798,
}
assert actual == nested_approx(expected)
And it fails with an appropriate message about how trial_angle is not a close enough number when the dict is in order:
def test_redux_this_fails_with_a_reasonable_error():
actual = {
'joint': { },
"trail_angle": 10.463173570164972,
}
expected = {
"joint": { },
'trail_angle': 10.593548226756798,
}
assert actual == nested_approx(expected)
But when the dict is in order and the numbers are failing, I get the really undesirable effect of comparing a float to a dict and I'm not sure why because I have done a pair-wise comparison over each of the keys.
Upvotes: 0
Views: 25