Skip to content

Commit 480b8e6

Browse files
Abseil Teamcopybara-github
Abseil Team
authored andcommitted
Add an assertDictContainsSubset method.
PiperOrigin-RevId: 723042884
1 parent da86f08 commit 480b8e6

File tree

3 files changed

+108
-1
lines changed

3 files changed

+108
-1
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ Nothing notable unreleased.
1212
tests equality of Mapping objects not requiring them to be dicts. Similar
1313
to `assertSequenceEqual` but for mappings.
1414

15+
* (testing) Added a new method `absltest.assertDictContainsSubset` that
16+
checks that a dictionary contains a subset of keys and values. Similar
17+
to a removed method `unittest.assertDictContainsSubset` (existed until Python 3.11).
18+
1519
### Fixed
1620

1721
* (testing) Fixed an issue where the test reporter crashes with exceptions with

absl/testing/absltest.py

+19
Original file line numberDiff line numberDiff line change
@@ -1656,6 +1656,25 @@ def CheckEqual(a, b):
16561656
for a, b in itertools.product(group, group):
16571657
CheckEqual(a, b)
16581658

1659+
def assertDictContainsSubset(
1660+
self, subset: Mapping[Any, Any], dictionary: Mapping[Any, Any], msg=None
1661+
):
1662+
"""Raises AssertionError if dictionary is not a superset of subset.
1663+
1664+
Args:
1665+
subset: A dict, the expected subset of the `dictionary`.
1666+
dictionary: A dict, the actual value.
1667+
msg: An optional str, the associated message.
1668+
1669+
Raises:
1670+
AssertionError: if dictionary is not a superset of subset.
1671+
"""
1672+
if not isinstance(subset, dict):
1673+
subset = dict(subset)
1674+
if not isinstance(dictionary, dict):
1675+
dictionary = dict(dictionary)
1676+
self.assertDictEqual(dictionary, {**dictionary, **subset}, msg)
1677+
16591678
def assertDictEqual(self, a, b, msg=None):
16601679
"""Raises AssertionError if a and b are not equal dictionaries.
16611680

absl/testing/tests/absltest_test.py

+85-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import sys
2828
import tempfile
2929
import textwrap
30-
from typing import Optional
30+
from typing import Any, ItemsView, Iterator, KeysView, Mapping, Optional, Type, ValuesView
3131
import unittest
3232

3333
from absl.testing import _bazelize_command
@@ -1597,6 +1597,90 @@ def test_assert_json_equal_bad_json(self):
15971597
with self.assertRaises(ValueError) as error_context:
15981598
self.assertJsonEqual('', '')
15991599

1600+
@parameterized.named_parameters(
1601+
dict(testcase_name='empty', subset={}, dictionary={}),
1602+
dict(testcase_name='empty_is_a_subset', subset={}, dictionary={'a': 1}),
1603+
dict(
1604+
testcase_name='equal_one_element',
1605+
subset={'a': 1},
1606+
dictionary={'a': 1},
1607+
),
1608+
dict(
1609+
testcase_name='subset', subset={'a': 1}, dictionary={'a': 1, 'b': 2}
1610+
),
1611+
dict(
1612+
testcase_name='equal_many_elements',
1613+
subset={'a': 1, 'b': 2},
1614+
dictionary={'a': 1, 'b': 2},
1615+
),
1616+
)
1617+
def test_assert_dict_contains_subset(
1618+
self, subset: Mapping[Any, Any], dictionary: Mapping[Any, Any]
1619+
):
1620+
self.assertDictContainsSubset(subset, dictionary)
1621+
1622+
def test_assert_dict_contains_subset_converts_to_dict(self):
1623+
class ConvertibleToDict(Mapping):
1624+
1625+
def __init__(self, **kwargs):
1626+
self._data = kwargs
1627+
1628+
def __getitem__(self, key: Any) -> Any:
1629+
return self._data[key]
1630+
1631+
def __iter__(self) -> Iterator:
1632+
return iter(self._data)
1633+
1634+
def __len__(self) -> int:
1635+
return len(self._data)
1636+
1637+
def keys(self) -> KeysView:
1638+
return self._data.keys()
1639+
1640+
def values(self) -> ValuesView:
1641+
return self._data.values()
1642+
1643+
def items(self) -> ItemsView:
1644+
return self._data.items()
1645+
1646+
self.assertDictContainsSubset(
1647+
ConvertibleToDict(name='a', value=1),
1648+
ConvertibleToDict(name='a', value=1),
1649+
)
1650+
1651+
@parameterized.named_parameters(
1652+
dict(testcase_name='subset_vs_empty', subset={1: 'one'}, dictionary={}),
1653+
dict(
1654+
testcase_name='value_is_different',
1655+
subset={'a': 2},
1656+
dictionary={'a': 1},
1657+
),
1658+
dict(
1659+
testcase_name='key_is_different', subset={'c': 1}, dictionary={'a': 1}
1660+
),
1661+
dict(
1662+
testcase_name='subset_is_larger',
1663+
subset={'a': 1, 'c': 1},
1664+
dictionary={'a': 1},
1665+
),
1666+
dict(
1667+
testcase_name='UnicodeDecodeError_constructing_failure_msg',
1668+
subset={'foo': ''.join(chr(i) for i in range(255))},
1669+
dictionary={'foo': '\uFFFD'},
1670+
),
1671+
)
1672+
def test_assert_dict_contains_subset_fails(
1673+
self, subset: Mapping[Any, Any], dictionary: Mapping[Any, Any]
1674+
):
1675+
with self.assertRaises(self.failureException):
1676+
self.assertDictContainsSubset(subset, dictionary)
1677+
1678+
def test_assert_dict_contains_subset_fails_with_msg(self):
1679+
with self.assertRaisesRegex(
1680+
AssertionError, re.compile('custom message', re.DOTALL)
1681+
):
1682+
self.assertDictContainsSubset({'a': 1}, {'a': 2}, msg='custom message')
1683+
16001684

16011685
class GetCommandStderrTestCase(absltest.TestCase):
16021686

0 commit comments

Comments
 (0)