Skip to content

Commit 1e394fe

Browse files
committed
Merge pull request #10507 from sinhrks/test_assert
TST: make assertion messages more understandable
2 parents 529288e + 4ed857e commit 1e394fe

File tree

5 files changed

+763
-93
lines changed

5 files changed

+763
-93
lines changed

pandas/io/tests/test_json/test_pandas.py

+21-7
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,10 @@ def _check_orient(df, orient, dtype=None, numpy=False,
178178
self.assertTrue(df.columns.equals(unser.columns))
179179
elif orient == "values":
180180
# index and cols are not captured in this orientation
181-
assert_almost_equal(df.values, unser.values)
181+
if numpy is True and df.shape == (0, 0):
182+
assert unser.shape[0] == 0
183+
else:
184+
assert_almost_equal(df.values, unser.values)
182185
elif orient == "split":
183186
# index and col labels might not be strings
184187
unser.index = [str(i) for i in unser.index]
@@ -670,15 +673,20 @@ def test_doc_example(self):
670673
def test_misc_example(self):
671674

672675
# parsing unordered input fails
673-
result = read_json('[{"a": 1, "b": 2}, {"b":2, "a" :1}]',numpy=True)
674-
expected = DataFrame([[1,2],[1,2]],columns=['a','b'])
675-
with tm.assertRaisesRegexp(AssertionError,
676-
'\[index\] left \[.+\], right \[.+\]'):
676+
result = read_json('[{"a": 1, "b": 2}, {"b":2, "a" :1}]', numpy=True)
677+
expected = DataFrame([[1,2], [1,2]], columns=['a', 'b'])
678+
679+
error_msg = """DataFrame\\.index are different
680+
681+
DataFrame\\.index values are different \\(100\\.0 %\\)
682+
\\[left\\]: Index\\(\\[u?'a', u?'b'\\], dtype='object'\\)
683+
\\[right\\]: Int64Index\\(\\[0, 1\\], dtype='int64'\\)"""
684+
with tm.assertRaisesRegexp(AssertionError, error_msg):
677685
assert_frame_equal(result, expected)
678686

679687
result = read_json('[{"a": 1, "b": 2}, {"b":2, "a" :1}]')
680-
expected = DataFrame([[1,2],[1,2]],columns=['a','b'])
681-
assert_frame_equal(result,expected)
688+
expected = DataFrame([[1,2], [1,2]], columns=['a','b'])
689+
assert_frame_equal(result, expected)
682690

683691
@network
684692
def test_round_trip_exception_(self):
@@ -739,3 +747,9 @@ def my_handler_raises(obj):
739747
raise TypeError("raisin")
740748
self.assertRaises(TypeError, DataFrame({'a': [1, 2, object()]}).to_json,
741749
default_handler=my_handler_raises)
750+
751+
752+
if __name__ == '__main__':
753+
import nose
754+
nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb',
755+
'--pdb-failure', '-s'], exit=False)

pandas/src/testing.pyx

+70-13
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,39 @@ cpdef assert_dict_equal(a, b, bint compare_keys=True):
5555

5656
return True
5757

58-
cpdef assert_almost_equal(a, b, bint check_less_precise=False):
58+
cpdef assert_almost_equal(a, b, bint check_less_precise=False,
59+
obj=None, lobj=None, robj=None):
60+
"""Check that left and right objects are almost equal.
61+
62+
Parameters
63+
----------
64+
a : object
65+
b : object
66+
check_less_precise : bool, default False
67+
Specify comparison precision.
68+
5 digits (False) or 3 digits (True) after decimal points are compared.
69+
obj : str, default None
70+
Specify object name being compared, internally used to show appropriate
71+
assertion message
72+
lobj : str, default None
73+
Specify left object name being compared, internally used to show
74+
appropriate assertion message
75+
robj : str, default None
76+
Specify right object name being compared, internally used to show
77+
appropriate assertion message
78+
"""
79+
5980
cdef:
6081
int decimal
82+
double diff = 0.0
6183
Py_ssize_t i, na, nb
6284
double fa, fb
85+
bint is_unequal = False
86+
87+
if lobj is None:
88+
lobj = a
89+
if robj is None:
90+
robj = b
6391

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

72100
if isiterable(a):
73-
assert isiterable(b), (
74-
"First object is iterable, second isn't: %r != %r" % (a, b)
75-
)
101+
102+
if not isiterable(b):
103+
from pandas.util.testing import raise_assert_detail
104+
if obj is None:
105+
obj = 'Iterable'
106+
msg = "First object is iterable, second isn't"
107+
raise_assert_detail(obj, msg, a, b)
108+
76109
assert has_length(a) and has_length(b), (
77110
"Can't compare objects without length, one or both is invalid: "
78111
"(%r, %r)" % (a, b)
79112
)
80113

81-
na, nb = len(a), len(b)
82-
assert na == nb, (
83-
"Length of two iterators not the same: %r != %r" % (na, nb)
84-
)
85114
if isinstance(a, np.ndarray) and isinstance(b, np.ndarray):
115+
if obj is None:
116+
obj = 'numpy array'
117+
na, nb = a.size, b.size
118+
if a.shape != b.shape:
119+
from pandas.util.testing import raise_assert_detail
120+
raise_assert_detail(obj, '{0} shapes are different'.format(obj),
121+
a.shape, b.shape)
86122
try:
87123
if np.array_equal(a, b):
88124
return True
89125
except:
90126
pass
127+
else:
128+
if obj is None:
129+
obj = 'Iterable'
130+
na, nb = len(a), len(b)
131+
132+
if na != nb:
133+
from pandas.util.testing import raise_assert_detail
134+
raise_assert_detail(obj, '{0} length are different'.format(obj),
135+
na, nb)
136+
137+
for i in xrange(len(a)):
138+
try:
139+
assert_almost_equal(a[i], b[i], check_less_precise)
140+
except AssertionError:
141+
is_unequal = True
142+
diff += 1
91143

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

95149
return True
150+
96151
elif isiterable(b):
97-
assert False, (
98-
"Second object is iterable, first isn't: %r != %r" % (a, b)
99-
)
152+
from pandas.util.testing import raise_assert_detail
153+
if obj is None:
154+
obj = 'Iterable'
155+
msg = "Second object is iterable, first isn't"
156+
raise_assert_detail(obj, msg, a, b)
100157

101158
if isnull(a):
102159
assert isnull(b), (

pandas/tests/test_index.py

+31-4
Original file line numberDiff line numberDiff line change
@@ -3371,7 +3371,10 @@ def test_inplace_mutation_resets_values(self):
33713371

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

47734776
mi = MultiIndex.from_product([list('ab'),range(3)],names=['first','second'])
47744777
str(mi)
4775-
tm.assert_index_equal(eval(repr(mi)),mi,exact=True)
4776-
4778+
4779+
if compat.PY3:
4780+
tm.assert_index_equal(eval(repr(mi)), mi, exact=True)
4781+
else:
4782+
result = eval(repr(mi))
4783+
# string coerces to unicode
4784+
tm.assert_index_equal(result, mi, exact=False)
4785+
self.assertEqual(mi.get_level_values('first').inferred_type, 'string')
4786+
self.assertEqual(result.get_level_values('first').inferred_type, 'unicode')
4787+
4788+
mi_u = MultiIndex.from_product([list(u'ab'),range(3)],names=['first','second'])
4789+
result = eval(repr(mi_u))
4790+
tm.assert_index_equal(result, mi_u, exact=True)
4791+
47774792
# formatting
47784793
if compat.PY3:
47794794
str(mi)
@@ -4783,7 +4798,19 @@ def test_repr_roundtrip(self):
47834798
# long format
47844799
mi = MultiIndex.from_product([list('abcdefg'),range(10)],names=['first','second'])
47854800
result = str(mi)
4786-
tm.assert_index_equal(eval(repr(mi)),mi,exact=True)
4801+
4802+
if compat.PY3:
4803+
tm.assert_index_equal(eval(repr(mi)), mi, exact=True)
4804+
else:
4805+
result = eval(repr(mi))
4806+
# string coerces to unicode
4807+
tm.assert_index_equal(result, mi, exact=False)
4808+
self.assertEqual(mi.get_level_values('first').inferred_type, 'string')
4809+
self.assertEqual(result.get_level_values('first').inferred_type, 'unicode')
4810+
4811+
mi = MultiIndex.from_product([list(u'abcdefg'),range(10)],names=['first','second'])
4812+
result = eval(repr(mi_u))
4813+
tm.assert_index_equal(result, mi_u, exact=True)
47874814

47884815
def test_str(self):
47894816
# tested elsewhere

0 commit comments

Comments
 (0)