Skip to content

Commit 58f1137

Browse files
committed
Merge pull request #4451 from jtratner/better-assert-tests
TST: pandas/util/testing : test improvements and cleanups
2 parents 6bbfe46 + b700f58 commit 58f1137

File tree

2 files changed

+135
-69
lines changed

2 files changed

+135
-69
lines changed

pandas/io/tests/test_pytables.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1763,7 +1763,10 @@ def test_index_types(self):
17631763

17641764
values = np.random.randn(2)
17651765

1766-
func = lambda l, r: tm.assert_series_equal(l, r, True, True, True)
1766+
func = lambda l, r: tm.assert_series_equal(l, r,
1767+
check_dtype=True,
1768+
check_index_type=True,
1769+
check_series_type=True)
17671770

17681771
with tm.assert_produces_warning(expected_warning=PerformanceWarning):
17691772
ser = Series(values, [0, 'y'])

pandas/util/testing.py

Lines changed: 131 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
from __future__ import division
2-
32
# pylint: disable-msg=W0402
43

54
import random
5+
import re
66
import string
77
import sys
88
import tempfile
@@ -11,7 +11,7 @@
1111
import os
1212

1313
from datetime import datetime
14-
from functools import wraps
14+
from functools import wraps, partial
1515
from contextlib import contextmanager
1616
from distutils.version import LooseVersion
1717

@@ -130,27 +130,38 @@ def assert_isinstance(obj, class_type_or_tuple):
130130
"Expected object to be of type %r, found %r instead" % (
131131
type(obj), class_type_or_tuple))
132132

133-
def assert_equal(actual, expected, msg=""):
134-
assert expected == actual, "%s: %r != %r" % (msg, actual, expected)
133+
def assert_equal(a, b, msg=""):
134+
"""asserts that a equals b, like nose's assert_equal, but allows custom message to start.
135+
Passes a and b to format string as well. So you can use '{0}' and '{1}' to display a and b.
136+
137+
Examples
138+
--------
139+
>>> assert_equal(2, 2, "apples")
140+
>>> assert_equal(5.2, 1.2, "{0} was really a dead parrot")
141+
Traceback (most recent call last):
142+
...
143+
AssertionError: 5.2 was really a dead parrot: 5.2 != 1.2
144+
"""
145+
assert a == b, "%s: %r != %r" % (msg.format(a,b), a, b)
146+
135147

136148
def assert_index_equal(left, right):
137149
if not left.equals(right):
138150
raise AssertionError("[index] left [{0} {1}], right [{2} {3}]".format(left.dtype,
139151
left,
140152
right,
141153
right.dtype))
154+
155+
142156
def assert_attr_equal(attr, left, right):
143-
left_attr = getattr(left, attr, None)
144-
right_attr = getattr(right, attr, None)
157+
"""checks attributes are equal. Both objects must have attribute."""
158+
left_attr = getattr(left, attr)
159+
right_attr = getattr(right, attr)
145160
assert_equal(left_attr,right_attr,"attr is not equal [{0}]" .format(attr))
146161

147162
def isiterable(obj):
148163
return hasattr(obj, '__iter__')
149164

150-
def assert_isinstance(obj, class_type_or_tuple):
151-
"""asserts that obj is an instance of class_type_or_tuple"""
152-
assert isinstance(obj, class_type_or_tuple), (
153-
"Expected object to be of type %r, found %r instead" % (type(obj), class_type_or_tuple))
154165

155166

156167
def assert_almost_equal(a, b, check_less_precise=False):
@@ -221,7 +232,6 @@ def assert_dict_equal(a, b, compare_keys=True):
221232

222233
def assert_series_equal(left, right, check_dtype=True,
223234
check_index_type=False,
224-
check_index_freq=False,
225235
check_series_type=False,
226236
check_less_precise=False):
227237
if check_series_type:
@@ -238,8 +248,6 @@ def assert_series_equal(left, right, check_dtype=True,
238248
assert_isinstance(left.index, type(right.index))
239249
assert_attr_equal('dtype', left.index, right.index)
240250
assert_attr_equal('inferred_type', left.index, right.index)
241-
if check_index_freq:
242-
assert_attr_equal('freqstr', left.index, right.index)
243251

244252

245253
def assert_frame_equal(left, right, check_dtype=True,
@@ -261,7 +269,7 @@ def assert_frame_equal(left, right, check_dtype=True,
261269
assert_index_equal(left.index, right.index)
262270

263271
for i, col in enumerate(left.columns):
264-
assert(col in right)
272+
assert col in right
265273
lcol = left.icol(i)
266274
rcol = right.icol(i)
267275
assert_series_equal(lcol, rcol,
@@ -282,44 +290,36 @@ def assert_frame_equal(left, right, check_dtype=True,
282290
assert_attr_equal('names', left.columns, right.columns)
283291

284292

285-
def assert_panel_equal(left, right,
286-
check_panel_type=False,
287-
check_less_precise=False):
293+
def assert_panelnd_equal(left, right,
294+
check_panel_type=False,
295+
check_less_precise=False,
296+
assert_func=assert_frame_equal):
288297
if check_panel_type:
289298
assert_isinstance(left, type(right))
290299

291300
for axis in ['items', 'major_axis', 'minor_axis']:
292-
assert_index_equal(
293-
getattr(left, axis, None), getattr(right, axis, None))
301+
left_ind = getattr(left, axis)
302+
right_ind = getattr(right, axis)
303+
assert_index_equal(left_ind, right_ind)
294304

295305
for col, series in compat.iteritems(left):
296-
assert(col in right)
297-
# TODO strangely check_names fails in py3 ?
298-
assert_frame_equal(
299-
series, right[col], check_less_precise=check_less_precise, check_names=False)
306+
assert col in right, "non-matching column '%s'" % col
307+
assert_func(series, right[col], check_less_precise=check_less_precise)
300308

301309
for col in right:
302-
assert(col in left)
303-
304-
305-
def assert_panel4d_equal(left, right,
306-
check_less_precise=False):
307-
for axis in ['labels', 'items', 'major_axis', 'minor_axis']:
308-
assert_index_equal(
309-
getattr(left, axis, None), getattr(right, axis, None))
310-
311-
for col, series in compat.iteritems(left):
312-
assert(col in right)
313-
assert_panel_equal(
314-
series, right[col], check_less_precise=check_less_precise)
310+
assert col in left
315311

316-
for col in right:
317-
assert(col in left)
312+
# TODO: strangely check_names fails in py3 ?
313+
_panel_frame_equal = partial(assert_frame_equal, check_names=False)
314+
assert_panel_equal = partial(assert_panelnd_equal,
315+
assert_func=_panel_frame_equal)
316+
assert_panel4d_equal = partial(assert_panelnd_equal,
317+
assert_func=assert_panel_equal)
318318

319319

320320
def assert_contains_all(iterable, dic):
321321
for k in iterable:
322-
assert(k in dic)
322+
assert k in dic, "Did not contain item: '%r'" % k
323323

324324

325325
def getCols(k):
@@ -986,7 +986,45 @@ def stdin_encoding(encoding=None):
986986
sys.stdin = _stdin
987987

988988

989-
def assertRaisesRegexp(exception, regexp, callable, *args, **kwargs):
989+
def assertRaises(_exception, _callable=None, *args, **kwargs):
990+
"""assertRaises that is usable as context manager or in a with statement
991+
992+
Exceptions that don't match the given Exception type fall through::
993+
994+
>>> with assertRaises(ValueError):
995+
... raise TypeError("banana")
996+
...
997+
Traceback (most recent call last):
998+
...
999+
TypeError: banana
1000+
1001+
If it raises the given Exception type, the test passes
1002+
>>> with assertRaises(KeyError):
1003+
... dct = dict()
1004+
... dct["apple"]
1005+
1006+
If the expected error doesn't occur, it raises an error.
1007+
>>> with assertRaises(KeyError):
1008+
... dct = {'apple':True}
1009+
... dct["apple"]
1010+
Traceback (most recent call last):
1011+
...
1012+
AssertionError: KeyError not raised.
1013+
1014+
In addition to using it as a contextmanager, you can also use it as a
1015+
function, just like the normal assertRaises
1016+
1017+
>>> assertRaises(TypeError, ",".join, [1, 3, 5]);
1018+
"""
1019+
manager = _AssertRaisesContextmanager(exception=_exception)
1020+
# don't return anything if usedin function form
1021+
if _callable is not None:
1022+
with manager:
1023+
_callable(*args, **kwargs)
1024+
else:
1025+
return manager
1026+
1027+
def assertRaisesRegexp(_exception, _regexp, _callable=None, *args, **kwargs):
9901028
""" Port of assertRaisesRegexp from unittest in Python 2.7 - used in with statement.
9911029
9921030
Explanation from standard library:
@@ -997,46 +1035,71 @@ def assertRaisesRegexp(exception, regexp, callable, *args, **kwargs):
9971035
9981036
You can pass either a regular expression or a compiled regular expression object.
9991037
>>> assertRaisesRegexp(ValueError, 'invalid literal for.*XYZ',
1000-
... int, 'XYZ')
1038+
... int, 'XYZ');
10011039
>>> import re
1002-
>>> assertRaisesRegexp(ValueError, re.compile('literal'), int, 'XYZ')
1040+
>>> assertRaisesRegexp(ValueError, re.compile('literal'), int, 'XYZ');
10031041
10041042
If an exception of a different type is raised, it bubbles up.
10051043
1006-
>>> assertRaisesRegexp(TypeError, 'literal', int, 'XYZ')
1044+
>>> assertRaisesRegexp(TypeError, 'literal', int, 'XYZ');
10071045
Traceback (most recent call last):
10081046
...
10091047
ValueError: invalid literal for int() with base 10: 'XYZ'
1010-
>>> dct = {}
1011-
>>> assertRaisesRegexp(KeyError, 'pear', dct.__getitem__, 'apple')
1048+
>>> dct = dict()
1049+
>>> assertRaisesRegexp(KeyError, 'pear', dct.__getitem__, 'apple');
10121050
Traceback (most recent call last):
10131051
...
10141052
AssertionError: "pear" does not match "'apple'"
1015-
>>> assertRaisesRegexp(KeyError, 'apple', dct.__getitem__, 'apple')
1016-
>>> assertRaisesRegexp(Exception, 'operand type.*int.*dict', lambda : 2 + {})
1017-
"""
1018-
1019-
import re
10201053
1021-
try:
1022-
callable(*args, **kwargs)
1023-
except Exception as e:
1024-
if not issubclass(e.__class__, exception):
1025-
# mimics behavior of unittest
1026-
raise
1027-
# don't recompile
1028-
if hasattr(regexp, "search"):
1029-
expected_regexp = regexp
1030-
else:
1031-
expected_regexp = re.compile(regexp)
1032-
if not expected_regexp.search(str(e)):
1033-
raise AssertionError('"%s" does not match "%s"' %
1034-
(expected_regexp.pattern, str(e)))
1054+
You can also use this in a with statement.
1055+
>>> with assertRaisesRegexp(TypeError, 'unsupported operand type\(s\)'):
1056+
... 1 + {}
1057+
>>> with assertRaisesRegexp(TypeError, 'banana'):
1058+
... 'apple'[0] = 'b'
1059+
Traceback (most recent call last):
1060+
...
1061+
AssertionError: "banana" does not match "'str' object does not support \
1062+
item assignment"
1063+
"""
1064+
manager = _AssertRaisesContextmanager(exception=_exception, regexp=_regexp)
1065+
if _callable is not None:
1066+
with manager:
1067+
_callable(*args, **kwargs)
10351068
else:
1036-
# Apparently some exceptions don't have a __name__ attribute? Just
1037-
# aping unittest library here
1038-
name = getattr(exception, "__name__", str(exception))
1039-
raise AssertionError("{0} not raised".format(name))
1069+
return manager
1070+
1071+
1072+
class _AssertRaisesContextmanager(object):
1073+
"""handles the behind the scenes work for assertRaises and assertRaisesRegexp"""
1074+
def __init__(self, exception, regexp=None, *args, **kwargs):
1075+
self.exception = exception
1076+
if regexp is not None and not hasattr(regexp, "search"):
1077+
regexp = re.compile(regexp)
1078+
self.regexp = regexp
1079+
1080+
def __enter__(self):
1081+
return self
1082+
1083+
def __exit__(self, exc_type, exc_value, traceback):
1084+
expected = self.exception
1085+
if not exc_type:
1086+
name = getattr(expected, "__name__", str(expected))
1087+
raise AssertionError("{0} not raised.".format(name))
1088+
if issubclass(exc_type, expected):
1089+
return self.handle_success(exc_type, exc_value, traceback)
1090+
return self.handle_failure(exc_type, exc_value, traceback)
1091+
1092+
def handle_failure(*args, **kwargs):
1093+
# Failed, so allow Exception to bubble up
1094+
return False
1095+
1096+
def handle_success(self, exc_type, exc_value, traceback):
1097+
if self.regexp is not None:
1098+
val = str(exc_value)
1099+
if not self.regexp.search(val):
1100+
raise AssertionError('"%s" does not match "%s"' %
1101+
(self.regexp.pattern, str(val)))
1102+
return True
10401103

10411104

10421105
@contextmanager

0 commit comments

Comments
 (0)