Skip to content
Open
1 change: 1 addition & 0 deletions changelog/13652.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
``contains one more item`` now reports the actual extra item in sequence diffs.
16 changes: 14 additions & 2 deletions src/_pytest/assertion/_compare_sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ def _compare_eq_sequence(
comparing_bytes = isinstance(left, bytes) and isinstance(right, bytes)
len_left = len(left)
len_right = len(right)
found_diff = False
i = -1
for i in range(min(len_left, len_right)):
if left[i] != right[i]:
if comparing_bytes:
Expand All @@ -73,6 +75,7 @@ def _compare_eq_sequence(
f"At index {i} diff:"
f" {highlighter(repr(left_value))} != {highlighter(repr(right_value))}"
)
found_diff = True
break

if comparing_bytes:
Expand All @@ -84,11 +87,20 @@ def _compare_eq_sequence(
if len_diff:
if len_diff > 0:
dir_with_more = "Left"
extra = saferepr(left[len_right])
# If the longer side has exactly one extra item and the tails after
# the first differing index align (offset by one), that item is the
# insertion.
if found_diff and len_diff == 1 and left[i + 1 :] == right[i:]:
extra = saferepr(left[i])
else:
extra = saferepr(left[len_right])
else:
len_diff = 0 - len_diff
dir_with_more = "Right"
extra = saferepr(right[len_left])
if found_diff and len_diff == 1 and right[i + 1 :] == left[i:]:
extra = saferepr(right[i])
else:
extra = saferepr(right[len_left])

if len_diff == 1:
yield f"{dir_with_more} contains one more item: {highlighter(extra)}"
Expand Down
28 changes: 28 additions & 0 deletions testing/test_assertion.py
Original file line number Diff line number Diff line change
Expand Up @@ -1105,6 +1105,34 @@ def test_sequence_different_items(self) -> None:
" )",
]

@pytest.mark.parametrize(
("left", "right", "expected"),
[
([], [1], "Right contains one more item: 1"),
([1], [], "Left contains one more item: 1"),
([1, 2, 3], [0, 1, 2, 3], "Right contains one more item: 0"),
([1, 2, 3], [1, 2, 3, 4], "Right contains one more item: 4"),
([1, 2, 3], [1, 2, 0, 3], "Right contains one more item: 0"),
([1, 2, 0, 3], [1, 2, 3], "Left contains one more item: 0"),
([1, 1], [1, 0, 1], "Right contains one more item: 0"),
([3, 4, 5], [1, 2], "Left contains one more item: 5"),
# Fallback: single extra item but tails don't align
([1, 2, 3], [1, 9, 8, 3], "Right contains one more item: 3"),
([1, 9, 8, 3], [1, 2, 3], "Left contains one more item: 3"),
(
[1, 2, 3],
[0, 1, 2, 3, 4],
"Right contains 2 more items, first extra item: 3",
),
],
)
def test_sequence_extra_item_message(
self, left: list[object], right: list[object], expected: str
) -> None:
lines = callequal(left, right, verbose=1)
assert lines is not None
assert expected in lines

def test_set(self) -> None:
expl = callequal({0, 1}, {0, 2})
assert expl is not None
Expand Down
46 changes: 45 additions & 1 deletion testing/test_error_diffs.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def test_this():
> assert result == expected
E assert [1, 3] == [1, 2, 3]
E At index 1 diff: 3 != 2
E Right contains one more item: 3
E Right contains one more item: 2
E Full diff: (-: missing in left side, +: extra in left side)
E [
E 1,
Expand All @@ -76,6 +76,50 @@ def test_this():
""",
id="Compare lists, one item missing",
),
pytest.param(
"""
def test_this():
result = [1, 2, 3]
expected = [1, 2, 0, 3]
assert result == expected
""",
"""
> assert result == expected
E assert [1, 2, 3] == [1, 2, 0, 3]
E At index 2 diff: 3 != 0
E Right contains one more item: 0
E Full diff: (-: missing in left side, +: extra in left side)
E [
E 1,
E 2,
E - 0,
E 3,
E ]
""",
id="Compare lists, one extra item inserted mid-list",
),
pytest.param(
"""
def test_this():
result = [1, 2, 0, 3]
expected = [1, 2, 3]
assert result == expected
""",
"""
> assert result == expected
E assert [1, 2, 0, 3] == [1, 2, 3]
E At index 2 diff: 0 != 3
E Left contains one more item: 0
E Full diff: (-: missing in left side, +: extra in left side)
E [
E 1,
E 2,
E + 0,
E 3,
E ]
""",
id="Compare lists, one extra item inserted mid-list on left",
),
pytest.param(
"""
def test_this():
Expand Down
Loading