The normal default pretty printer is not great when objects are nested and it can get hard to read the diff. Instead, provide a pretty printer that behaves more like when json get indented, which allows for smaller, more meaningful differences, at the expense of a slightly longer diff. This does not touch the other places where the pretty printer is used, and only updated the full diff one.
294 lines
8.1 KiB
Python
294 lines
8.1 KiB
Python
"""
|
|
Tests and examples for correct "+/-" usage in error diffs.
|
|
|
|
See https://github.com/pytest-dev/pytest/issues/3333 for details.
|
|
|
|
"""
|
|
import pytest
|
|
from _pytest.pytester import Pytester
|
|
|
|
|
|
TESTCASES = [
|
|
pytest.param(
|
|
"""
|
|
def test_this():
|
|
result = [1, 4, 3]
|
|
expected = [1, 2, 3]
|
|
assert result == expected
|
|
""",
|
|
"""
|
|
> assert result == expected
|
|
E assert [1, 4, 3] == [1, 2, 3]
|
|
E At index 1 diff: 4 != 2
|
|
E Full diff:
|
|
E [
|
|
E 1,
|
|
E - 2,
|
|
E ? ^
|
|
E + 4,
|
|
E ? ^
|
|
E 3,
|
|
E ]
|
|
""",
|
|
id="Compare lists, one item differs",
|
|
),
|
|
pytest.param(
|
|
"""
|
|
def test_this():
|
|
result = [1, 2, 3]
|
|
expected = [1, 2]
|
|
assert result == expected
|
|
""",
|
|
"""
|
|
> assert result == expected
|
|
E assert [1, 2, 3] == [1, 2]
|
|
E Left contains one more item: 3
|
|
E Full diff:
|
|
E [
|
|
E 1,
|
|
E 2,
|
|
E + 3,
|
|
E ]
|
|
""",
|
|
id="Compare lists, one extra item",
|
|
),
|
|
pytest.param(
|
|
"""
|
|
def test_this():
|
|
result = [1, 3]
|
|
expected = [1, 2, 3]
|
|
assert result == expected
|
|
""",
|
|
"""
|
|
> 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 Full diff:
|
|
E [
|
|
E 1,
|
|
E - 2,
|
|
E 3,
|
|
E ]
|
|
""",
|
|
id="Compare lists, one item missing",
|
|
),
|
|
pytest.param(
|
|
"""
|
|
def test_this():
|
|
result = (1, 4, 3)
|
|
expected = (1, 2, 3)
|
|
assert result == expected
|
|
""",
|
|
"""
|
|
> assert result == expected
|
|
E assert (1, 4, 3) == (1, 2, 3)
|
|
E At index 1 diff: 4 != 2
|
|
E Full diff:
|
|
E (
|
|
E 1,
|
|
E - 2,
|
|
E ? ^
|
|
E + 4,
|
|
E ? ^
|
|
E 3,
|
|
E )
|
|
""",
|
|
id="Compare tuples",
|
|
),
|
|
pytest.param(
|
|
"""
|
|
def test_this():
|
|
result = {1, 3, 4}
|
|
expected = {1, 2, 3}
|
|
assert result == expected
|
|
""",
|
|
"""
|
|
> assert result == expected
|
|
E assert {1, 3, 4} == {1, 2, 3}
|
|
E Extra items in the left set:
|
|
E 4
|
|
E Extra items in the right set:
|
|
E 2
|
|
E Full diff:
|
|
E {
|
|
E 1,
|
|
E - 2,
|
|
E 3,
|
|
E + 4,
|
|
E }
|
|
""",
|
|
id="Compare sets",
|
|
),
|
|
pytest.param(
|
|
"""
|
|
def test_this():
|
|
result = {1: 'spam', 3: 'eggs'}
|
|
expected = {1: 'spam', 2: 'eggs'}
|
|
assert result == expected
|
|
""",
|
|
"""
|
|
> assert result == expected
|
|
E AssertionError: assert {1: 'spam', 3: 'eggs'} == {1: 'spam', 2: 'eggs'}
|
|
E Common items:
|
|
E {1: 'spam'}
|
|
E Left contains 1 more item:
|
|
E {3: 'eggs'}
|
|
E Right contains 1 more item:
|
|
E {2: 'eggs'}
|
|
E Full diff:
|
|
E {
|
|
E 1: 'spam',
|
|
E - 2: 'eggs',
|
|
E ? ^
|
|
E + 3: 'eggs',
|
|
E ? ^
|
|
E }
|
|
""",
|
|
id="Compare dicts with differing keys",
|
|
),
|
|
pytest.param(
|
|
"""
|
|
def test_this():
|
|
result = {1: 'spam', 2: 'eggs'}
|
|
expected = {1: 'spam', 2: 'bacon'}
|
|
assert result == expected
|
|
""",
|
|
"""
|
|
> assert result == expected
|
|
E AssertionError: assert {1: 'spam', 2: 'eggs'} == {1: 'spam', 2: 'bacon'}
|
|
E Common items:
|
|
E {1: 'spam'}
|
|
E Differing items:
|
|
E {2: 'eggs'} != {2: 'bacon'}
|
|
E Full diff:
|
|
E {
|
|
E 1: 'spam',
|
|
E - 2: 'bacon',
|
|
E + 2: 'eggs',
|
|
E }
|
|
""",
|
|
id="Compare dicts with differing values",
|
|
),
|
|
pytest.param(
|
|
"""
|
|
def test_this():
|
|
result = {1: 'spam', 2: 'eggs'}
|
|
expected = {1: 'spam', 3: 'bacon'}
|
|
assert result == expected
|
|
""",
|
|
"""
|
|
> assert result == expected
|
|
E AssertionError: assert {1: 'spam', 2: 'eggs'} == {1: 'spam', 3: 'bacon'}
|
|
E Common items:
|
|
E {1: 'spam'}
|
|
E Left contains 1 more item:
|
|
E {2: 'eggs'}
|
|
E Right contains 1 more item:
|
|
E {3: 'bacon'}
|
|
E Full diff:
|
|
E {
|
|
E 1: 'spam',
|
|
E - 3: 'bacon',
|
|
E + 2: 'eggs',
|
|
E }
|
|
""",
|
|
id="Compare dicts with differing items",
|
|
),
|
|
pytest.param(
|
|
"""
|
|
def test_this():
|
|
result = "spmaeggs"
|
|
expected = "spameggs"
|
|
assert result == expected
|
|
""",
|
|
"""
|
|
> assert result == expected
|
|
E AssertionError: assert 'spmaeggs' == 'spameggs'
|
|
E - spameggs
|
|
E ? -
|
|
E + spmaeggs
|
|
E ? +
|
|
""",
|
|
id="Compare strings",
|
|
),
|
|
pytest.param(
|
|
"""
|
|
def test_this():
|
|
result = "spam bacon eggs"
|
|
assert "bacon" not in result
|
|
""",
|
|
"""
|
|
> assert "bacon" not in result
|
|
E AssertionError: assert 'bacon' not in 'spam bacon eggs'
|
|
E 'bacon' is contained here:
|
|
E spam bacon eggs
|
|
E ? +++++
|
|
""",
|
|
id='Test "not in" string',
|
|
),
|
|
pytest.param(
|
|
"""
|
|
from dataclasses import dataclass
|
|
|
|
@dataclass
|
|
class A:
|
|
a: int
|
|
b: str
|
|
|
|
def test_this():
|
|
result = A(1, 'spam')
|
|
expected = A(2, 'spam')
|
|
assert result == expected
|
|
""",
|
|
"""
|
|
> assert result == expected
|
|
E AssertionError: assert A(a=1, b='spam') == A(a=2, b='spam')
|
|
E Matching attributes:
|
|
E ['b']
|
|
E Differing attributes:
|
|
E ['a']
|
|
E Drill down into differing attribute a:
|
|
E a: 1 != 2
|
|
""",
|
|
id="Compare data classes",
|
|
),
|
|
pytest.param(
|
|
"""
|
|
import attr
|
|
|
|
@attr.s(auto_attribs=True)
|
|
class A:
|
|
a: int
|
|
b: str
|
|
|
|
def test_this():
|
|
result = A(1, 'spam')
|
|
expected = A(1, 'eggs')
|
|
assert result == expected
|
|
""",
|
|
"""
|
|
> assert result == expected
|
|
E AssertionError: assert A(a=1, b='spam') == A(a=1, b='eggs')
|
|
E Matching attributes:
|
|
E ['a']
|
|
E Differing attributes:
|
|
E ['b']
|
|
E Drill down into differing attribute b:
|
|
E b: 'spam' != 'eggs'
|
|
E - eggs
|
|
E + spam
|
|
""",
|
|
id="Compare attrs classes",
|
|
),
|
|
]
|
|
|
|
|
|
@pytest.mark.parametrize("code, expected", TESTCASES)
|
|
def test_error_diff(code: str, expected: str, pytester: Pytester) -> None:
|
|
expected_lines = [line.lstrip() for line in expected.splitlines()]
|
|
p = pytester.makepyfile(code)
|
|
result = pytester.runpytest(p, "-vv")
|
|
result.stdout.fnmatch_lines(expected_lines)
|
|
assert result.ret == 1
|