Skip to content

TST: make assertion messages more understandable #10507

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 10, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions pandas/io/tests/test_json/test_pandas.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,10 @@ def _check_orient(df, orient, dtype=None, numpy=False,
self.assertTrue(df.columns.equals(unser.columns))
elif orient == "values":
# index and cols are not captured in this orientation
assert_almost_equal(df.values, unser.values)
if numpy is True and df.shape == (0, 0):
assert unser.shape[0] == 0
else:
assert_almost_equal(df.values, unser.values)
elif orient == "split":
# index and col labels might not be strings
unser.index = [str(i) for i in unser.index]
Expand Down Expand Up @@ -670,15 +673,20 @@ def test_doc_example(self):
def test_misc_example(self):

# parsing unordered input fails
result = read_json('[{"a": 1, "b": 2}, {"b":2, "a" :1}]',numpy=True)
expected = DataFrame([[1,2],[1,2]],columns=['a','b'])
with tm.assertRaisesRegexp(AssertionError,
'\[index\] left \[.+\], right \[.+\]'):
result = read_json('[{"a": 1, "b": 2}, {"b":2, "a" :1}]', numpy=True)
expected = DataFrame([[1,2], [1,2]], columns=['a', 'b'])

error_msg = """DataFrame\\.index are different

DataFrame\\.index values are different \\(100\\.0 %\\)
\\[left\\]: Index\\(\\[u?'a', u?'b'\\], dtype='object'\\)
\\[right\\]: Int64Index\\(\\[0, 1\\], dtype='int64'\\)"""
with tm.assertRaisesRegexp(AssertionError, error_msg):
assert_frame_equal(result, expected)

result = read_json('[{"a": 1, "b": 2}, {"b":2, "a" :1}]')
expected = DataFrame([[1,2],[1,2]],columns=['a','b'])
assert_frame_equal(result,expected)
expected = DataFrame([[1,2], [1,2]], columns=['a','b'])
assert_frame_equal(result, expected)

@network
def test_round_trip_exception_(self):
Expand Down Expand Up @@ -739,3 +747,9 @@ def my_handler_raises(obj):
raise TypeError("raisin")
self.assertRaises(TypeError, DataFrame({'a': [1, 2, object()]}).to_json,
default_handler=my_handler_raises)


if __name__ == '__main__':
import nose
nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb',
'--pdb-failure', '-s'], exit=False)
83 changes: 70 additions & 13 deletions pandas/src/testing.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,39 @@ cpdef assert_dict_equal(a, b, bint compare_keys=True):

return True

cpdef assert_almost_equal(a, b, bint check_less_precise=False):
cpdef assert_almost_equal(a, b, bint check_less_precise=False,
obj=None, lobj=None, robj=None):
"""Check that left and right objects are almost equal.

Parameters
----------
a : object
b : object
check_less_precise : bool, default False
Specify comparison precision.
5 digits (False) or 3 digits (True) after decimal points are compared.
obj : str, default None
Specify object name being compared, internally used to show appropriate
assertion message
lobj : str, default None
Specify left object name being compared, internally used to show
appropriate assertion message
robj : str, default None
Specify right object name being compared, internally used to show
appropriate assertion message
"""

cdef:
int decimal
double diff = 0.0
Py_ssize_t i, na, nb
double fa, fb
bint is_unequal = False

if lobj is None:
lobj = a
if robj is None:
robj = b

if isinstance(a, dict) or isinstance(b, dict):
return assert_dict_equal(a, b)
Expand All @@ -70,33 +98,62 @@ cpdef assert_almost_equal(a, b, bint check_less_precise=False):
return True

if isiterable(a):
assert isiterable(b), (
"First object is iterable, second isn't: %r != %r" % (a, b)
)

if not isiterable(b):
from pandas.util.testing import raise_assert_detail
if obj is None:
obj = 'Iterable'
msg = "First object is iterable, second isn't"
raise_assert_detail(obj, msg, a, b)

assert has_length(a) and has_length(b), (
"Can't compare objects without length, one or both is invalid: "
"(%r, %r)" % (a, b)
)

na, nb = len(a), len(b)
assert na == nb, (
"Length of two iterators not the same: %r != %r" % (na, nb)
)
if isinstance(a, np.ndarray) and isinstance(b, np.ndarray):
if obj is None:
obj = 'numpy array'
na, nb = a.size, b.size
if a.shape != b.shape:
from pandas.util.testing import raise_assert_detail
raise_assert_detail(obj, '{0} shapes are different'.format(obj),
a.shape, b.shape)
try:
if np.array_equal(a, b):
return True
except:
pass
else:
if obj is None:
obj = 'Iterable'
na, nb = len(a), len(b)

if na != nb:
from pandas.util.testing import raise_assert_detail
raise_assert_detail(obj, '{0} length are different'.format(obj),
na, nb)

for i in xrange(len(a)):
try:
assert_almost_equal(a[i], b[i], check_less_precise)
except AssertionError:
is_unequal = True
diff += 1

for i in xrange(na):
assert_almost_equal(a[i], b[i], check_less_precise)
if is_unequal:
from pandas.util.testing import raise_assert_detail
msg = '{0} values are different ({1} %)'.format(obj, np.round(diff * 100.0 / na, 5))
raise_assert_detail(obj, msg, lobj, robj)

return True

elif isiterable(b):
assert False, (
"Second object is iterable, first isn't: %r != %r" % (a, b)
)
from pandas.util.testing import raise_assert_detail
if obj is None:
obj = 'Iterable'
msg = "Second object is iterable, first isn't"
raise_assert_detail(obj, msg, a, b)

if isnull(a):
assert isnull(b), (
Expand Down
35 changes: 31 additions & 4 deletions pandas/tests/test_index.py
Original file line number Diff line number Diff line change
Expand Up @@ -3371,7 +3371,10 @@ def test_inplace_mutation_resets_values(self):

# make sure label setting works too
labels2 = [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]]
exp_values = np.array([(long(1), 'a')] * 6, dtype=object)
exp_values = np.empty((6, ), dtype=object)
exp_values[:] = [(long(1), 'a')] * 6
# must be 1d array of tuples
self.assertEqual(exp_values.shape, (6, ))
new_values = mi2.set_labels(labels2).values
# not inplace shouldn't change
assert_almost_equal(mi2._tuples, vals2)
Expand Down Expand Up @@ -4772,8 +4775,20 @@ def test_repr_roundtrip(self):

mi = MultiIndex.from_product([list('ab'),range(3)],names=['first','second'])
str(mi)
tm.assert_index_equal(eval(repr(mi)),mi,exact=True)


if compat.PY3:
tm.assert_index_equal(eval(repr(mi)), mi, exact=True)
else:
result = eval(repr(mi))
# string coerces to unicode
tm.assert_index_equal(result, mi, exact=False)
self.assertEqual(mi.get_level_values('first').inferred_type, 'string')
self.assertEqual(result.get_level_values('first').inferred_type, 'unicode')

mi_u = MultiIndex.from_product([list(u'ab'),range(3)],names=['first','second'])
result = eval(repr(mi_u))
tm.assert_index_equal(result, mi_u, exact=True)

# formatting
if compat.PY3:
str(mi)
Expand All @@ -4783,7 +4798,19 @@ def test_repr_roundtrip(self):
# long format
mi = MultiIndex.from_product([list('abcdefg'),range(10)],names=['first','second'])
result = str(mi)
tm.assert_index_equal(eval(repr(mi)),mi,exact=True)

if compat.PY3:
tm.assert_index_equal(eval(repr(mi)), mi, exact=True)
else:
result = eval(repr(mi))
# string coerces to unicode
tm.assert_index_equal(result, mi, exact=False)
self.assertEqual(mi.get_level_values('first').inferred_type, 'string')
self.assertEqual(result.get_level_values('first').inferred_type, 'unicode')

mi = MultiIndex.from_product([list(u'abcdefg'),range(10)],names=['first','second'])
result = eval(repr(mi_u))
tm.assert_index_equal(result, mi_u, exact=True)

def test_str(self):
# tested elsewhere
Expand Down
Loading