Skip to content

Commit 040ec86

Browse files
committed
Add assert_dict_superset()
Closes: #19
1 parent 6c93c6f commit 040ec86

4 files changed

Lines changed: 117 additions & 1 deletion

File tree

NEWS.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ API Additions
55
-------------
66

77
* Add ``assert_dict_equal()``.
8+
* Add ``assert_dict_superset()``.
89

910
Improvements
1011
------------

asserts/__init__.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,62 @@ def assert_dict_equal(first, second, key_msg_fmt="{msg}",
357357
assert_equal(first_value, second_value, msg_fmt=msg)
358358

359359

360+
def assert_dict_superset(first, second, key_msg_fmt="{msg}",
361+
value_msg_fmt="{msg}"):
362+
"""Fail unless second dictionary is a superset of the first.
363+
364+
The second dictionary must contain all keys of the first and their
365+
values are equal (or a superset in case of dicts). But the second
366+
dictionary can contain additional keys.
367+
368+
>>> assert_equal({"foo": 5}, {"foo": 5, "bar": 10})
369+
>>> assert_equal({"foo": 5}, {})
370+
Traceback (most recent call last):
371+
...
372+
AssertionError: key 'foo' missing from right dict
373+
374+
The following key_msg_fmt arguments are supported, if the keys do not
375+
match:
376+
* msg - the default error message
377+
* first - the first dict
378+
* second - the second dict
379+
* missing_keys - list of keys missing from right
380+
381+
The following value_msg_fmt arguments are supported, if a value does not
382+
match:
383+
* msg - the default error message
384+
* first - the first dict
385+
* second - the second dict
386+
* key - the key where the value does not match
387+
* first_value - the value in the first dict
388+
* second_value - the value in the second dict
389+
"""
390+
first_keys = set(first.keys())
391+
second_keys = set(second.keys())
392+
missing_keys = list(first_keys - second_keys)
393+
if missing_keys:
394+
if len(missing_keys) == 1:
395+
msg = "key {!r} missing from right dict".format(missing_keys[0])
396+
else:
397+
keys = ", ".join(sorted(repr(k) for k in missing_keys))
398+
msg = "keys {} missing from right dict".format(keys)
399+
if key_msg_fmt:
400+
msg = key_msg_fmt.format(
401+
msg=msg, first=first, second=second, missing_keys=missing_keys)
402+
raise AssertionError(msg)
403+
for key in first:
404+
first_value = first[key]
405+
second_value = second[key]
406+
msg = "key '{}' differs: {!r} != {!r}".format(
407+
key, first_value, second_value)
408+
if value_msg_fmt:
409+
msg = value_msg_fmt.format(
410+
msg=msg, first=first, second=second,
411+
key=key, first_value=first_value, second_value=second_value)
412+
msg = msg.replace("{", "{{").replace("}", "}}")
413+
assert_equal(first_value, second_value, msg_fmt=msg)
414+
415+
360416
def assert_less(first, second, msg_fmt="{msg}"):
361417
"""Fail if first is not less than second.
362418

asserts/__init__.pyi

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ 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: ...
4949
def assert_dict_equal(first: dict, second: dict, key_msg_fmt: Text = ..., value_msg_fmt: Text = ...) -> None: ...
50+
def assert_dict_superset(first: dict, second: dict, key_msg_fmt: Text = ..., value_msg_fmt: Text = ...) -> None: ...
5051
def assert_less(first: Any, second: Any, msg_fmt: Text = ...) -> None: ...
5152
def assert_less_equal(first: Any, second: Any, msg_fmt: Text = ...) -> None: ...
5253
def assert_greater(first: Any, second: Any, msg_fmt: Text = ...) -> None: ...

test_asserts.py

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
assert_not_equal,
1717
assert_almost_equal,
1818
assert_not_almost_equal,
19+
assert_dict_equal,
20+
assert_dict_superset,
1921
assert_less,
2022
assert_less_equal,
2123
assert_greater,
@@ -37,7 +39,8 @@
3739
assert_raises_errno,
3840
assert_succeeds,
3941
assert_warns,
40-
assert_warns_regex, assert_dict_equal)
42+
assert_warns_regex,
43+
)
4144

4245

4346
class _DummyObject(object):
@@ -437,6 +440,61 @@ def test_assert_dict_equal__custom_value_message(self):
437440
"{key!r};{first_value};{second_value}"
438441
)
439442

443+
# assert_dict_superset()
444+
445+
def test_assert_dict_superset__empty_dicts(self):
446+
assert_dict_superset({}, {})
447+
448+
def test_assert_dict_superset__dicts_are_equal(self):
449+
assert_dict_superset({"foo": 5}, {"foo": 5})
450+
451+
def test_assert_dict_superset__dicts_is_superset(self):
452+
assert_dict_superset({"foo": 5}, {"foo": 5, "bar": 10})
453+
454+
def test_assert_dict_superset__one_key_missing_from_right(self):
455+
with _assert_raises_assertion("key 'foo' missing from right dict"):
456+
assert_dict_superset({"bar": 10, "foo": 5}, {"bar": 10})
457+
458+
def test_assert_dict_superset__multiple_keys_missing_from_right(self):
459+
with _assert_raises_assertion(
460+
"keys 'bar', 'foo' missing from right dict"):
461+
assert_dict_superset({"foo": 5, "bar": 10, "baz": 15}, {"baz": 15})
462+
463+
def test_assert_dict_superset__values_do_not_match(self):
464+
with _assert_raises_assertion("key 'foo' differs: 15 != 10"):
465+
assert_dict_superset({"foo": 15}, {"foo": 10, "bar": 15})
466+
467+
def test_assert_dict_superset__not_string_keys(self):
468+
with _assert_raises_assertion("key 10 missing from right dict"):
469+
assert_dict_superset({10: "foo"}, {})
470+
with _assert_raises_assertion(
471+
"keys 'foo', 5 missing from right dict"):
472+
assert_dict_superset({5: "", "foo": ""}, {})
473+
474+
def test_assert_dict_superset__message_precedence(self):
475+
with _assert_raises_assertion("key 'foo' missing from right dict"):
476+
assert_dict_superset({"foo": "", "bar": 5}, {"bar": 1})
477+
478+
def test_assert_dict_superset__custom_key_message(self):
479+
with _assert_raises_assertion(
480+
"key 'foo' missing from right dict;"
481+
"{'foo': ''};{'bar': ''};['foo']"):
482+
assert_dict_superset(
483+
{"foo": ""}, {"bar": ""},
484+
key_msg_fmt="{msg};{first!r};{second!r};"
485+
"{missing_keys!r}"
486+
)
487+
488+
def test_assert_dict_superset__custom_value_message(self):
489+
with _assert_raises_assertion(
490+
"key 'foo' differs: 5 != 10;{'foo': 5};{'foo': 10};"
491+
"'foo';5;10"):
492+
assert_dict_superset(
493+
{"foo": 5}, {"foo": 10},
494+
value_msg_fmt="{msg};{first!r};{second!r};"
495+
"{key!r};{first_value};{second_value}"
496+
)
497+
440498
# assert_less()
441499

442500
def test_assert_less(self):

0 commit comments

Comments
 (0)