Merged in bubenkoff/pytest/better-diff-on-verbose-2 (pull request #213)
Improve assertion failure reporting on iterables, by using ndiff and pprint.
This commit is contained in:
commit
f05cb934a9
|
@ -1,3 +1,8 @@
|
||||||
|
Unreleased
|
||||||
|
----------
|
||||||
|
|
||||||
|
- Improve assertion failure reporting on iterables, by using ndiff and pprint.
|
||||||
|
|
||||||
2.6.3
|
2.6.3
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
|
|
|
@ -135,18 +135,32 @@ def assertrepr_compare(config, op, left, right):
|
||||||
isdict = lambda x: isinstance(x, dict)
|
isdict = lambda x: isinstance(x, dict)
|
||||||
isset = lambda x: isinstance(x, (set, frozenset))
|
isset = lambda x: isinstance(x, (set, frozenset))
|
||||||
|
|
||||||
|
def isiterable(obj):
|
||||||
|
try:
|
||||||
|
iter(obj)
|
||||||
|
return not istext(obj)
|
||||||
|
except TypeError:
|
||||||
|
return False
|
||||||
|
|
||||||
verbose = config.getoption('verbose')
|
verbose = config.getoption('verbose')
|
||||||
explanation = None
|
explanation = None
|
||||||
try:
|
try:
|
||||||
if op == '==':
|
if op == '==':
|
||||||
if istext(left) and istext(right):
|
if istext(left) and istext(right):
|
||||||
explanation = _diff_text(left, right, verbose)
|
explanation = _diff_text(left, right, verbose)
|
||||||
elif issequence(left) and issequence(right):
|
else:
|
||||||
|
if issequence(left) and issequence(right):
|
||||||
explanation = _compare_eq_sequence(left, right, verbose)
|
explanation = _compare_eq_sequence(left, right, verbose)
|
||||||
elif isset(left) and isset(right):
|
elif isset(left) and isset(right):
|
||||||
explanation = _compare_eq_set(left, right, verbose)
|
explanation = _compare_eq_set(left, right, verbose)
|
||||||
elif isdict(left) and isdict(right):
|
elif isdict(left) and isdict(right):
|
||||||
explanation = _compare_eq_dict(left, right, verbose)
|
explanation = _compare_eq_dict(left, right, verbose)
|
||||||
|
if isiterable(left) and isiterable(right):
|
||||||
|
expl = _compare_eq_iterable(left, right, verbose)
|
||||||
|
if explanation is not None:
|
||||||
|
explanation.extend(expl)
|
||||||
|
else:
|
||||||
|
explanation = expl
|
||||||
elif op == 'not in':
|
elif op == 'not in':
|
||||||
if istext(left) and istext(right):
|
if istext(left) and istext(right):
|
||||||
explanation = _notin_text(left, right, verbose)
|
explanation = _notin_text(left, right, verbose)
|
||||||
|
@ -203,6 +217,19 @@ def _diff_text(left, right, verbose=False):
|
||||||
return explanation
|
return explanation
|
||||||
|
|
||||||
|
|
||||||
|
def _compare_eq_iterable(left, right, verbose=False):
|
||||||
|
if not verbose:
|
||||||
|
return [u('Use -v to get the full diff')]
|
||||||
|
# dynamic import to speedup pytest
|
||||||
|
import difflib
|
||||||
|
|
||||||
|
left = pprint.pformat(left).splitlines()
|
||||||
|
right = pprint.pformat(right).splitlines()
|
||||||
|
explanation = [u('Full diff:')]
|
||||||
|
explanation.extend(line.strip() for line in difflib.ndiff(left, right))
|
||||||
|
return explanation
|
||||||
|
|
||||||
|
|
||||||
def _compare_eq_sequence(left, right, verbose=False):
|
def _compare_eq_sequence(left, right, verbose=False):
|
||||||
explanation = []
|
explanation = []
|
||||||
for i in range(min(len(left), len(right))):
|
for i in range(min(len(left), len(right))):
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import sys
|
import sys
|
||||||
|
import textwrap
|
||||||
|
|
||||||
import py, pytest
|
import py, pytest
|
||||||
import _pytest.assertion as plugin
|
import _pytest.assertion as plugin
|
||||||
from _pytest.assertion import reinterpret
|
from _pytest.assertion import reinterpret
|
||||||
from _pytest.assertion import util
|
from _pytest.assertion import util
|
||||||
|
|
||||||
needsnewassert = pytest.mark.skipif("sys.version_info < (2,6)")
|
needsnewassert = pytest.mark.skipif("sys.version_info < (2,6)")
|
||||||
|
PY3 = sys.version_info >= (3, 0)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
|
@ -86,6 +89,48 @@ class TestAssert_reprcompare:
|
||||||
expl = callequal([0, 1], [0, 2])
|
expl = callequal([0, 1], [0, 2])
|
||||||
assert len(expl) > 1
|
assert len(expl) > 1
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
['left', 'right', 'expected'], [
|
||||||
|
([0, 1], [0, 2], """
|
||||||
|
Full diff:
|
||||||
|
- [0, 1]
|
||||||
|
? ^
|
||||||
|
+ [0, 2]
|
||||||
|
? ^
|
||||||
|
"""),
|
||||||
|
({0: 1}, {0: 2}, """
|
||||||
|
Full diff:
|
||||||
|
- {0: 1}
|
||||||
|
? ^
|
||||||
|
+ {0: 2}
|
||||||
|
? ^
|
||||||
|
"""),
|
||||||
|
(set([0, 1]), set([0, 2]), """
|
||||||
|
Full diff:
|
||||||
|
- set([0, 1])
|
||||||
|
? ^
|
||||||
|
+ set([0, 2])
|
||||||
|
? ^
|
||||||
|
""" if not PY3 else """
|
||||||
|
Full diff:
|
||||||
|
- {0, 1}
|
||||||
|
? ^
|
||||||
|
+ {0, 2}
|
||||||
|
? ^
|
||||||
|
""")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
def test_iterable_full_diff(self, left, right, expected):
|
||||||
|
"""Test the full diff assertion failure explanation.
|
||||||
|
|
||||||
|
When verbose is False, then just a -v notice to get the diff is rendered,
|
||||||
|
when verbose is True, then ndiff of the pprint is returned.
|
||||||
|
"""
|
||||||
|
expl = callequal(left, right, verbose=False)
|
||||||
|
assert expl[-1] == 'Use -v to get the full diff'
|
||||||
|
expl = '\n'.join(callequal(left, right, verbose=True))
|
||||||
|
assert expl.endswith(textwrap.dedent(expected).strip())
|
||||||
|
|
||||||
def test_list_different_lenghts(self):
|
def test_list_different_lenghts(self):
|
||||||
expl = callequal([0, 1], [0, 1, 2])
|
expl = callequal([0, 1], [0, 1, 2])
|
||||||
assert len(expl) > 1
|
assert len(expl) > 1
|
||||||
|
|
Loading…
Reference in New Issue