Skip to content

Commit 40c6e4b

Browse files
committed
Improve set comparison output in assertEqual
Fixes #108
1 parent 723114a commit 40c6e4b

File tree

2 files changed

+67
-0
lines changed

2 files changed

+67
-0
lines changed

testtools/matchers/_basic.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,14 @@ def __init__(self, actual, mismatch_string, reference, reference_on_right=True):
7373
self._reference_on_right = reference_on_right
7474

7575
def describe(self):
76+
# Special handling for set comparisons
77+
if (
78+
self._mismatch_string == "!="
79+
and isinstance(self._reference, set)
80+
and isinstance(self._actual, set)
81+
):
82+
return self._describe_set_difference()
83+
7684
actual = repr(self._actual)
7785
reference = repr(self._reference)
7886
if len(actual) + len(reference) > 70:
@@ -88,6 +96,27 @@ def describe(self):
8896
left, right = reference, actual
8997
return f"{left} {self._mismatch_string} {right}"
9098

99+
def _describe_set_difference(self):
100+
"""Describe the difference between two sets in a readable format."""
101+
reference_only = sorted(
102+
self._reference - self._actual, key=lambda x: (type(x).__name__, x)
103+
)
104+
actual_only = sorted(
105+
self._actual - self._reference, key=lambda x: (type(x).__name__, x)
106+
)
107+
108+
lines = ["!=:"]
109+
if reference_only:
110+
lines.append(
111+
f"Items in expected but not in actual:\n{_format(reference_only)}"
112+
)
113+
if actual_only:
114+
lines.append(
115+
f"Items in actual but not in expected:\n{_format(actual_only)}"
116+
)
117+
118+
return "\n".join(lines)
119+
91120

92121
class Equals(_BinaryComparison):
93122
"""Matches if the items are equal."""

testtools/tests/test_testcase.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,44 @@ def test_assertEqual_non_ascii_str_with_newlines(self):
863863
)
864864
self.assertFails(expected_error, self.assertEqual, a, b, message)
865865

866+
def test_assertEqual_set_difference(self):
867+
a = {1, 2, 3, 4}
868+
b = {2, 3, 5, 6}
869+
expected_error = "\n".join(
870+
[
871+
"!=:",
872+
"Items in expected but not in actual:",
873+
"[1, 4]",
874+
"Items in actual but not in expected:",
875+
"[5, 6]",
876+
]
877+
)
878+
self.assertFails(expected_error, self.assertEqual, a, b)
879+
880+
def test_assertEqual_set_missing_items_only(self):
881+
a = {1, 2, 3, 4}
882+
b = {2, 3}
883+
expected_error = "\n".join(
884+
[
885+
"!=:",
886+
"Items in expected but not in actual:",
887+
"[1, 4]",
888+
]
889+
)
890+
self.assertFails(expected_error, self.assertEqual, a, b)
891+
892+
def test_assertEqual_set_extra_items_only(self):
893+
a = {1, 2}
894+
b = {1, 2, 3, 4}
895+
expected_error = "\n".join(
896+
[
897+
"!=:",
898+
"Items in actual but not in expected:",
899+
"[3, 4]",
900+
]
901+
)
902+
self.assertFails(expected_error, self.assertEqual, a, b)
903+
866904
def test_assertIsNone(self):
867905
self.assertIsNone(None)
868906

0 commit comments

Comments
 (0)