Improve assertion failure reporting on iterables, by using ndiff and pprint.

--HG--
branch : better-diff-on-verbose-2
This commit is contained in:
Anatoly Bubenkov 2014-09-27 01:29:47 +00:00
parent 49b7237581
commit 72e6f55b45
3 changed files with 94 additions and 17 deletions

View File

@ -1,3 +1,8 @@
Unreleased
----------
- Improve assertion failure reporting on iterables, by using ndiff and pprint.
2.6.3 2.6.3
----------- -----------

View File

@ -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))):

View File

@ -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