Skip to content

Commit 6c93c6f

Browse files
committed
Merge branch '14-assert-dict-equal'
2 parents 6f2dcc0 + 53cc758 commit 6c93c6f

4 files changed

Lines changed: 149 additions & 3 deletions

File tree

NEWS.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
News in asserts 0.8.4
22
=====================
33

4+
API Additions
5+
-------------
6+
7+
* Add ``assert_dict_equal()``.
8+
49
Improvements
510
------------
611

7-
* `fail()` is now marked with `NoReturn` in type stub.
12+
* ``assert_equal()``: Use ``assert_dict_equal()`` if applicable.
13+
* ``fail()`` is now marked with ``NoReturn`` in type stub.
814

915
Bug Fixes
1016
---------

asserts/__init__.py

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,9 @@ def assert_equal(first, second, msg_fmt="{msg}"):
165165
* second - the second argument
166166
"""
167167

168-
if not first == second:
168+
if isinstance(first, dict) and isinstance(second, dict):
169+
assert_dict_equal(first, second, msg_fmt)
170+
elif not first == second:
169171
msg = "{!r} != {!r}".format(first, second)
170172
fail(msg_fmt.format(msg=msg, first=first, second=second))
171173

@@ -290,6 +292,71 @@ def assert_not_almost_equal(first, second, msg_fmt="{msg}",
290292
places=places, delta=delta))
291293

292294

295+
def assert_dict_equal(first, second, key_msg_fmt="{msg}",
296+
value_msg_fmt="{msg}"):
297+
"""Fail unless first dictionary equals second.
298+
299+
The dictionaries are considered equal, if they both contain the same
300+
keys, and their respective values are also equal.
301+
302+
>>> assert_equal({"foo": 5}, {"foo": 5})
303+
>>> assert_equal({"foo": 5}, {})
304+
Traceback (most recent call last):
305+
...
306+
AssertionError: key 'foo' missing from right dict
307+
308+
The following key_msg_fmt arguments are supported, if the keys do not
309+
match:
310+
* msg - the default error message
311+
* first - the first dict
312+
* second - the second dict
313+
* missing_keys - list of keys missing from right
314+
* extra_keys - list of keys missing from left
315+
316+
The following value_msg_fmt arguments are supported, if a value does not
317+
match:
318+
* msg - the default error message
319+
* first - the first dict
320+
* second - the second dict
321+
* key - the key where the value does not match
322+
* first_value - the value in the first dict
323+
* second_value - the value in the second dict
324+
"""
325+
first_keys = set(first.keys())
326+
second_keys = set(second.keys())
327+
missing_keys = list(first_keys - second_keys)
328+
extra_keys = list(second_keys - first_keys)
329+
if missing_keys or extra_keys:
330+
if missing_keys:
331+
if len(missing_keys) == 1:
332+
msg = "key {!r} missing from right dict".format(missing_keys[0])
333+
else:
334+
keys = ", ".join(sorted(repr(k) for k in missing_keys))
335+
msg = "keys {} missing from right dict".format(keys)
336+
else:
337+
if len(extra_keys) == 1:
338+
msg = "extra key {!r} in right dict".format(extra_keys[0])
339+
else:
340+
keys = ", ".join(sorted(repr(k) for k in extra_keys))
341+
msg = "extra keys {} in right dict".format(keys)
342+
if key_msg_fmt:
343+
msg = key_msg_fmt.format(
344+
msg=msg, first=first, second=second,
345+
missing_keys=missing_keys, extra_keys=extra_keys)
346+
raise AssertionError(msg)
347+
for key in first:
348+
first_value = first[key]
349+
second_value = second[key]
350+
msg = "key '{}' differs: {!r} != {!r}".format(
351+
key, first_value, second_value)
352+
if value_msg_fmt:
353+
msg = value_msg_fmt.format(
354+
msg=msg, first=first, second=second,
355+
key=key, first_value=first_value, second_value=second_value)
356+
msg = msg.replace("{", "{{").replace("}", "}}")
357+
assert_equal(first_value, second_value, msg_fmt=msg)
358+
359+
293360
def assert_less(first, second, msg_fmt="{msg}"):
294361
"""Fail if first is not less than second.
295362

asserts/__init__.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ def assert_equal(first: Any, second: Any, msg_fmt: Text = ...) -> None: ...
4646
def assert_not_equal(first: Any, second: Any, msg_fmt: Text = ...) -> None: ...
4747
def assert_almost_equal(first: float, second: float, msg_fmt: Text = ..., places: int = ..., delta: float = ...) -> None: ...
4848
def assert_not_almost_equal(first: float, second: float, msg_fmt: Text = ..., places: int = ..., delta: float = ...) -> None: ...
49+
def assert_dict_equal(first: dict, second: dict, key_msg_fmt: Text = ..., value_msg_fmt: Text = ...) -> None: ...
4950
def assert_less(first: Any, second: Any, msg_fmt: Text = ...) -> None: ...
5051
def assert_less_equal(first: Any, second: Any, msg_fmt: Text = ...) -> None: ...
5152
def assert_greater(first: Any, second: Any, msg_fmt: Text = ...) -> None: ...

test_asserts.py

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
assert_raises_errno,
3838
assert_succeeds,
3939
assert_warns,
40-
assert_warns_regex)
40+
assert_warns_regex, assert_dict_equal)
4141

4242

4343
class _DummyObject(object):
@@ -187,6 +187,10 @@ def test_assert_equal__not_equal__custom_message(self):
187187
with _assert_raises_assertion("'string' != 55;'string';55"):
188188
assert_equal("string", 55, "{msg};{first!r};{second!r}")
189189

190+
def test_assert_equal__dict(self):
191+
with _assert_raises_assertion("key 'foo' missing from right dict"):
192+
assert_equal({"foo": 5}, {})
193+
190194
# assert_not_equal()
191195

192196
def test_assert_not_equal__not_equal(self):
@@ -365,6 +369,74 @@ def test_not_assert_almost_equal__delta_lt_0(self):
365369
else:
366370
raise AssertionError("ValueError not raised")
367371

372+
# assert_dict_equal()
373+
374+
def test_assert_dict_equal__empty_dicts(self):
375+
assert_dict_equal({}, {})
376+
377+
def test_assert_dict_equal__dicts_are_equal(self):
378+
assert_dict_equal({"foo": 5}, {"foo": 5})
379+
380+
def test_assert_dict_equal__one_key_missing_from_right(self):
381+
with _assert_raises_assertion("key 'foo' missing from right dict"):
382+
assert_dict_equal({"bar": 10, "foo": 5}, {"bar": 10})
383+
384+
def test_assert_dict_equal__multiple_keys_missing_from_right(self):
385+
with _assert_raises_assertion(
386+
"keys 'bar', 'foo' missing from right dict"):
387+
assert_dict_equal({"foo": 5, "bar": 10, "baz": 15}, {"baz": 15})
388+
389+
def test_assert_dict_equal__one_key_missing_from_left(self):
390+
with _assert_raises_assertion("extra key 'foo' in right dict"):
391+
assert_dict_equal({"bar": 10}, {"bar": 10, "foo": 5})
392+
393+
def test_assert_dict_equal__multiple_keys_missing_from_left(self):
394+
with _assert_raises_assertion("extra keys 'bar', 'foo' in right dict"):
395+
assert_dict_equal({"baz": 15}, {"foo": 5, "bar": 10, "baz": 15})
396+
397+
def test_assert_dict_equal__values_do_not_match(self):
398+
with _assert_raises_assertion("key 'foo' differs: 15 != 10"):
399+
assert_dict_equal({"foo": 15}, {"foo": 10})
400+
401+
def test_assert_dict_equal__not_string_keys(self):
402+
with _assert_raises_assertion("key 10 missing from right dict"):
403+
assert_dict_equal({10: "foo"}, {})
404+
with _assert_raises_assertion(
405+
"keys 'foo', 5 missing from right dict"):
406+
assert_dict_equal({5: "", "foo": ""}, {})
407+
with _assert_raises_assertion("extra key 10 in right dict"):
408+
assert_dict_equal({}, {10: "foo"})
409+
with _assert_raises_assertion("extra keys 'foo', 5 in right dict"):
410+
assert_dict_equal({}, {5: "", "foo": ""})
411+
412+
def test_assert_dict_equal__message_precedence(self):
413+
with _assert_raises_assertion("key 'foo' missing from right dict"):
414+
assert_dict_equal({"foo": "", "bar": "", "baz": 5},
415+
{"bar": "", "baz": 10, "extra": ""})
416+
with _assert_raises_assertion("extra key 'extra' in right dict"):
417+
assert_dict_equal({"bar": "", "baz": 5},
418+
{"bar": "", "baz": 10, "extra": ""})
419+
420+
def test_assert_dict_equal__custom_key_message(self):
421+
with _assert_raises_assertion(
422+
"key 'foo' missing from right dict;"
423+
"{'foo': ''};{'bar': ''};['foo'];['bar']"):
424+
assert_dict_equal(
425+
{"foo": ""}, {"bar": ""},
426+
key_msg_fmt="{msg};{first!r};{second!r};"
427+
"{missing_keys!r};{extra_keys!r}"
428+
)
429+
430+
def test_assert_dict_equal__custom_value_message(self):
431+
with _assert_raises_assertion(
432+
"key 'foo' differs: 5 != 10;{'foo': 5};{'foo': 10};"
433+
"'foo';5;10"):
434+
assert_dict_equal(
435+
{"foo": 5}, {"foo": 10},
436+
value_msg_fmt="{msg};{first!r};{second!r};"
437+
"{key!r};{first_value};{second_value}"
438+
)
439+
368440
# assert_less()
369441

370442
def test_assert_less(self):

0 commit comments

Comments
 (0)