Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 41 additions & 3 deletions Lib/test/test_unittest/test_assertions.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,47 @@ def testAssertNotIn(self):

def testAssertDictEqual(self):
self.assertMessages('assertDictEqual', ({}, {'key': 'value'}),
[r"\+ \{'key': 'value'\}$", "^oops$",
r"\+ \{'key': 'value'\}$",
r"\+ \{'key': 'value'\} : oops$"])
[r"^\{\} != \{'key': 'value'\}\n\{\nKeys in the second "
r"dict but not the first:\n \+ 'key': 'value',\n\}$",
r"^oops$",
r"^\{\} != \{'key': 'value'\}\n\{\nKeys in the second "
r"dict but not the first:\n \+ 'key': 'value',\n\}$",
r"^\{\} != \{'key': 'value'\}\n\{\nKeys in the second "
r"dict but not the first:\n \+ 'key': 'value',\n\} : oops$"])
self.assertDictEqual({}, {})
self.assertDictEqual({'key': 'value'}, {'key': 'value'})
self.assertRaisesRegex(
AssertionError,
r"^\{\} != \{'key': 'value'\}\n{\nKeys in the second "
r"dict but not the first:\n \+ 'key': 'value',\n}$",
lambda: self.assertDictEqual({}, {'key': 'value'}))
self.assertRaisesRegex(
AssertionError,
r"^\{'key': 'value'\} != \{\}\n{\nKeys in the first "
r"dict but not the second:\n - 'key': 'value',\n}$",
lambda: self.assertDictEqual({'key': 'value'}, {}))
self.assertRaisesRegex(
AssertionError,
r"^\{'key': 'value'\} != \{'key': 'othervalue'\}\n{\nKeys in both dicts "
r"with differing values:\n - 'key': 'value',\n \+ 'key': 'othervalue',\n}$",
lambda: self.assertDictEqual({'key': 'value'}, {'key': 'othervalue'}))
self.assertRaisesRegex(
AssertionError,
r"^\{'same': 'same', 'samekey': 'onevalue', 'otherkey': 'othervalue'\} "
r"!= \{'same': 'same', 'samekey': 'twovalue', 'somekey': 'somevalue'\}\n{\n"
r" 'same': 'same',\n"
r"Keys in both dicts with differing values:\n"
r" - 'samekey': 'onevalue',\n"
r" \+ 'samekey': 'twovalue',\n"
r"Keys in the first dict but not the second:\n"
r" - 'otherkey': 'othervalue',\n"
r"Keys in the second dict but not the first:\n"
r" \+ 'somekey': 'somevalue',\n"
r"\}$",
lambda: self.assertDictEqual(
{'same': 'same', 'samekey': 'onevalue', 'otherkey': 'othervalue'},
{'same': 'same', 'samekey': 'twovalue', 'somekey': 'somevalue'}))


def testAssertMultiLineEqual(self):
self.assertMessages('assertMultiLineEqual', ("", "foo"),
Expand Down
49 changes: 42 additions & 7 deletions Lib/unittest/case.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@

from . import result
from .util import (strclass, safe_repr, _count_diff_all_purpose,
_count_diff_hashable, _common_shorten_repr)
_count_diff_hashable, _common_shorten_repr,
_shorten, _MIN_END_LEN, _MAX_LENGTH)

__unittest = True

Expand Down Expand Up @@ -1203,14 +1204,48 @@ def assertIsNot(self, expr1, expr2, msg=None):
self.fail(self._formatMessage(msg, standardMsg))

def assertDictEqual(self, d1, d2, msg=None):
self.assertIsInstance(d1, dict, 'First argument is not a dictionary')
self.assertIsInstance(d2, dict, 'Second argument is not a dictionary')
self.assertIsInstance(d1, dict, "First argument is not a dictionary")
self.assertIsInstance(d2, dict, "Second argument is not a dictionary")

if d1 != d2:
standardMsg = '%s != %s' % _common_shorten_repr(d1, d2)
diff = ('\n' + '\n'.join(difflib.ndiff(
pprint.pformat(d1).splitlines(),
pprint.pformat(d2).splitlines())))
standardMsg = "%s != %s" % _common_shorten_repr(d1, d2)

d1keys = set(d1.keys())
d2keys = set(d2.keys())
d1extrakeys = d1keys - d2keys
d2extrakeys = d2keys - d1keys
commonkeys = d1keys & d2keys
lines = []
def _value_repr(value):
return _shorten(safe_repr(value), _MAX_LENGTH//2-_MIN_END_LEN, _MIN_END_LEN)
def _justified_values(d, keys, prefix):
items = [(_value_repr(key), _value_repr(d[key])) for key in sorted(keys)]
justify_width = max(len(key) for key, value in items)
justify_width = max(min(justify_width, _MAX_LENGTH - _MIN_END_LEN - 2), 4)
return (" %s %s: %s," % (prefix, key.ljust(justify_width), value) for key, value in items)
if commonkeys:
commonvalues = []
for key in sorted(commonkeys):
if d1[key] == d2[key]:
commonvalues.append(key)
commonkeys.remove(key)
if commonvalues:
lines.extend(_justified_values(d1, commonvalues, " "))
if commonkeys:
lines.append("Keys in both dicts with differing values:")
for key in sorted(commonkeys):
key_repr = _value_repr(key)
lines.append(" - %s: %s," % (key_repr, _value_repr(d1[key])))
lines.append(" + %s: %s," % (key_repr, _value_repr(d2[key])))
if d1extrakeys:
lines.append("Keys in the first dict but not the second:")
lines.extend(_justified_values(d1, d1extrakeys, "-"))
if d2extrakeys:
lines.append("Keys in the second dict but not the first:")
lines.extend(_justified_values(d2, d2extrakeys, "+"))

diff = "\n{\n%s\n}" % '\n'.join(lines)

standardMsg = self._truncateMessage(standardMsg, diff)
self.fail(self._formatMessage(msg, standardMsg))

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Improve performance and error readability of
:meth:`~unittest.TestCase.assertDictEqual`.
Loading