Skip to content

Commit ae4c1af

Browse files
authored
Merge pull request #492 from testing-cabal/set-eq
Improve set comparison output in assertEqual
2 parents 339332f + 40c6e4b commit ae4c1af

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
@@ -74,6 +74,14 @@ def __init__(self, actual, mismatch_string, reference, reference_on_right=True):
7474
self._reference_on_right = reference_on_right
7575

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

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

93122
class Equals(_BinaryComparison):
94123
"""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)