Skip to content

Commit 9bbfe99

Browse files
authored
Add more comprehensive set assertion rewrites (#11469)
Fixes #10617
1 parent d015bc1 commit 9bbfe99

File tree

3 files changed

+131
-47
lines changed

3 files changed

+131
-47
lines changed

changelog/10617.feature.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Added more comprehensive set assertion rewrites for comparisons other than equality ``==``, with
2+
the following operations now providing better failure messages: ``!=``, ``<=``, ``>=``, ``<``, and ``>``.

src/_pytest/assertion/util.py

Lines changed: 59 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,22 @@ def assertrepr_compare(
193193
elif op == "not in":
194194
if istext(left) and istext(right):
195195
explanation = _notin_text(left, right, verbose)
196+
elif op == "!=":
197+
if isset(left) and isset(right):
198+
explanation = ["Both sets are equal"]
199+
elif op == ">=":
200+
if isset(left) and isset(right):
201+
explanation = _compare_gte_set(left, right, verbose)
202+
elif op == "<=":
203+
if isset(left) and isset(right):
204+
explanation = _compare_lte_set(left, right, verbose)
205+
elif op == ">":
206+
if isset(left) and isset(right):
207+
explanation = _compare_gt_set(left, right, verbose)
208+
elif op == "<":
209+
if isset(left) and isset(right):
210+
explanation = _compare_lt_set(left, right, verbose)
211+
196212
except outcomes.Exit:
197213
raise
198214
except Exception:
@@ -392,15 +408,49 @@ def _compare_eq_set(
392408
left: AbstractSet[Any], right: AbstractSet[Any], verbose: int = 0
393409
) -> List[str]:
394410
explanation = []
395-
diff_left = left - right
396-
diff_right = right - left
397-
if diff_left:
398-
explanation.append("Extra items in the left set:")
399-
for item in diff_left:
400-
explanation.append(saferepr(item))
401-
if diff_right:
402-
explanation.append("Extra items in the right set:")
403-
for item in diff_right:
411+
explanation.extend(_set_one_sided_diff("left", left, right))
412+
explanation.extend(_set_one_sided_diff("right", right, left))
413+
return explanation
414+
415+
416+
def _compare_gt_set(
417+
left: AbstractSet[Any], right: AbstractSet[Any], verbose: int = 0
418+
) -> List[str]:
419+
explanation = _compare_gte_set(left, right, verbose)
420+
if not explanation:
421+
return ["Both sets are equal"]
422+
return explanation
423+
424+
425+
def _compare_lt_set(
426+
left: AbstractSet[Any], right: AbstractSet[Any], verbose: int = 0
427+
) -> List[str]:
428+
explanation = _compare_lte_set(left, right, verbose)
429+
if not explanation:
430+
return ["Both sets are equal"]
431+
return explanation
432+
433+
434+
def _compare_gte_set(
435+
left: AbstractSet[Any], right: AbstractSet[Any], verbose: int = 0
436+
) -> List[str]:
437+
return _set_one_sided_diff("right", right, left)
438+
439+
440+
def _compare_lte_set(
441+
left: AbstractSet[Any], right: AbstractSet[Any], verbose: int = 0
442+
) -> List[str]:
443+
return _set_one_sided_diff("left", left, right)
444+
445+
446+
def _set_one_sided_diff(
447+
posn: str, set1: AbstractSet[Any], set2: AbstractSet[Any]
448+
) -> List[str]:
449+
explanation = []
450+
diff = set1 - set2
451+
if diff:
452+
explanation.append(f"Extra items in the {posn} set:")
453+
for item in diff:
404454
explanation.append(saferepr(item))
405455
return explanation
406456

testing/test_assertion.py

Lines changed: 70 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,48 +1345,80 @@ def test_reprcompare_whitespaces() -> None:
13451345
]
13461346

13471347

1348-
def test_pytest_assertrepr_compare_integration(pytester: Pytester) -> None:
1349-
pytester.makepyfile(
1348+
class TestSetAssertions:
1349+
@pytest.mark.parametrize("op", [">=", ">", "<=", "<", "=="])
1350+
def test_set_extra_item(self, op, pytester: Pytester) -> None:
1351+
pytester.makepyfile(
1352+
f"""
1353+
def test_hello():
1354+
x = set("hello x")
1355+
y = set("hello y")
1356+
assert x {op} y
13501357
"""
1351-
def test_hello():
1352-
x = set(range(100))
1353-
y = x.copy()
1354-
y.remove(50)
1355-
assert x == y
1356-
"""
1357-
)
1358-
result = pytester.runpytest()
1359-
result.stdout.fnmatch_lines(
1360-
[
1361-
"*def test_hello():*",
1362-
"*assert x == y*",
1363-
"*E*Extra items*left*",
1364-
"*E*50*",
1365-
"*= 1 failed in*",
1366-
]
1367-
)
1358+
)
13681359

1360+
result = pytester.runpytest()
1361+
result.stdout.fnmatch_lines(
1362+
[
1363+
"*def test_hello():*",
1364+
f"*assert x {op} y*",
1365+
]
1366+
)
1367+
if op in [">=", ">", "=="]:
1368+
result.stdout.fnmatch_lines(
1369+
[
1370+
"*E*Extra items in the right set:*",
1371+
"*E*'y'",
1372+
]
1373+
)
1374+
if op in ["<=", "<", "=="]:
1375+
result.stdout.fnmatch_lines(
1376+
[
1377+
"*E*Extra items in the left set:*",
1378+
"*E*'x'",
1379+
]
1380+
)
1381+
1382+
@pytest.mark.parametrize("op", [">", "<", "!="])
1383+
def test_set_proper_superset_equal(self, pytester: Pytester, op) -> None:
1384+
pytester.makepyfile(
1385+
f"""
1386+
def test_hello():
1387+
x = set([1, 2, 3])
1388+
y = x.copy()
1389+
assert x {op} y
1390+
"""
1391+
)
13691392

1370-
def test_sequence_comparison_uses_repr(pytester: Pytester) -> None:
1371-
pytester.makepyfile(
1393+
result = pytester.runpytest()
1394+
result.stdout.fnmatch_lines(
1395+
[
1396+
"*def test_hello():*",
1397+
f"*assert x {op} y*",
1398+
"*E*Both sets are equal*",
1399+
]
1400+
)
1401+
1402+
def test_pytest_assertrepr_compare_integration(self, pytester: Pytester) -> None:
1403+
pytester.makepyfile(
1404+
"""
1405+
def test_hello():
1406+
x = set(range(100))
1407+
y = x.copy()
1408+
y.remove(50)
1409+
assert x == y
13721410
"""
1373-
def test_hello():
1374-
x = set("hello x")
1375-
y = set("hello y")
1376-
assert x == y
1377-
"""
1378-
)
1379-
result = pytester.runpytest()
1380-
result.stdout.fnmatch_lines(
1381-
[
1382-
"*def test_hello():*",
1383-
"*assert x == y*",
1384-
"*E*Extra items*left*",
1385-
"*E*'x'*",
1386-
"*E*Extra items*right*",
1387-
"*E*'y'*",
1388-
]
1389-
)
1411+
)
1412+
result = pytester.runpytest()
1413+
result.stdout.fnmatch_lines(
1414+
[
1415+
"*def test_hello():*",
1416+
"*assert x == y*",
1417+
"*E*Extra items*left*",
1418+
"*E*50*",
1419+
"*= 1 failed in*",
1420+
]
1421+
)
13901422

13911423

13921424
def test_assertrepr_loaded_per_dir(pytester: Pytester) -> None:

0 commit comments

Comments
 (0)