blob: ff6b8b3cb961991b4ffbd1cc4105e86a549022f0 [file] [log] [blame]
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +00001"""Test case implementation"""
2
3import sys
4import functools
5import difflib
6import pprint
7import re
8import warnings
9
Michael Foord225a0992010-02-18 20:30:09 +000010from . import result
Michael Foord98e7b762010-03-20 03:00:34 +000011from .util import (
12 strclass, safe_repr, sorted_list_difference, unorderable_list_difference
13)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000014
Michael Foordb1aa30f2010-03-22 00:06:30 +000015__unittest = True
Michael Foordb1aa30f2010-03-22 00:06:30 +000016
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000017class SkipTest(Exception):
18 """
19 Raise this exception in a test to skip it.
20
21 Usually you can use TestResult.skip() or one of the skipping decorators
22 instead of raising this directly.
23 """
24 pass
25
26class _ExpectedFailure(Exception):
27 """
28 Raise this when a test is expected to fail.
29
30 This is an implementation detail.
31 """
32
33 def __init__(self, exc_info):
34 super(_ExpectedFailure, self).__init__()
35 self.exc_info = exc_info
36
37class _UnexpectedSuccess(Exception):
38 """
39 The test was supposed to fail, but it didn't!
40 """
41 pass
42
43def _id(obj):
44 return obj
45
46def skip(reason):
47 """
48 Unconditionally skip a test.
49 """
50 def decorator(test_item):
Michael Foord53e8eea2010-03-07 20:22:12 +000051 if not (isinstance(test_item, type) and issubclass(test_item, TestCase)):
52 @functools.wraps(test_item)
53 def skip_wrapper(*args, **kwargs):
54 raise SkipTest(reason)
55 test_item = skip_wrapper
56
57 test_item.__unittest_skip__ = True
58 test_item.__unittest_skip_why__ = reason
59 return test_item
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000060 return decorator
61
62def skipIf(condition, reason):
63 """
64 Skip a test if the condition is true.
65 """
66 if condition:
67 return skip(reason)
68 return _id
69
70def skipUnless(condition, reason):
71 """
72 Skip a test unless the condition is true.
73 """
74 if not condition:
75 return skip(reason)
76 return _id
77
78
79def expectedFailure(func):
80 @functools.wraps(func)
81 def wrapper(*args, **kwargs):
82 try:
83 func(*args, **kwargs)
84 except Exception:
85 raise _ExpectedFailure(sys.exc_info())
86 raise _UnexpectedSuccess
87 return wrapper
88
89
90class _AssertRaisesContext(object):
91 """A context manager used to implement TestCase.assertRaises* methods."""
92
93 def __init__(self, expected, test_case, expected_regexp=None):
94 self.expected = expected
95 self.failureException = test_case.failureException
Georg Brandlb0eb4d32010-02-07 11:34:15 +000096 self.expected_regexp = expected_regexp
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000097
98 def __enter__(self):
Michael Foord2bd52dc2010-02-07 18:44:12 +000099 return self
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000100
101 def __exit__(self, exc_type, exc_value, tb):
102 if exc_type is None:
103 try:
104 exc_name = self.expected.__name__
105 except AttributeError:
106 exc_name = str(self.expected)
107 raise self.failureException(
108 "{0} not raised".format(exc_name))
109 if not issubclass(exc_type, self.expected):
110 # let unexpected exceptions pass through
111 return False
Georg Brandldc3694b2010-02-07 17:02:22 +0000112 self.exception = exc_value # store for later retrieval
Georg Brandlb0eb4d32010-02-07 11:34:15 +0000113 if self.expected_regexp is None:
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000114 return True
115
Georg Brandlb0eb4d32010-02-07 11:34:15 +0000116 expected_regexp = self.expected_regexp
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000117 if isinstance(expected_regexp, basestring):
118 expected_regexp = re.compile(expected_regexp)
119 if not expected_regexp.search(str(exc_value)):
120 raise self.failureException('"%s" does not match "%s"' %
121 (expected_regexp.pattern, str(exc_value)))
122 return True
123
124
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000125class TestCase(object):
126 """A class whose instances are single test cases.
127
128 By default, the test code itself should be placed in a method named
129 'runTest'.
130
131 If the fixture may be used for many test cases, create as
132 many test methods as are needed. When instantiating such a TestCase
133 subclass, specify in the constructor arguments the name of the test method
134 that the instance is to execute.
135
136 Test authors should subclass TestCase for their own tests. Construction
137 and deconstruction of the test's environment ('fixture') can be
138 implemented by overriding the 'setUp' and 'tearDown' methods respectively.
139
140 If it is necessary to override the __init__ method, the base class
141 __init__ method must always be called. It is important that subclasses
142 should not change the signature of their __init__ method, since instances
143 of the classes are instantiated automatically by parts of the framework
144 in order to be run.
145 """
146
147 # This attribute determines which exception will be raised when
148 # the instance's assertion methods fail; test methods raising this
149 # exception will be deemed to have 'failed' rather than 'errored'
150
151 failureException = AssertionError
152
153 # This attribute determines whether long messages (including repr of
154 # objects used in assert methods) will be printed on failure in *addition*
155 # to any explicit message passed.
156
157 longMessage = False
158
Michael Foorde37d75f2010-06-05 12:10:52 +0000159 # This attribute sets the maximum length of a diff in failure messsages
160 # by assert methods using difflib. It is looked up as an instance attribute
161 # so can be configured by individual tests if required.
162 maxDiff = 80*8
163
Michael Foord5ffa3252010-03-07 22:04:55 +0000164 # Attribute used by TestSuite for classSetUp
165
166 _classSetupFailed = False
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000167
168 def __init__(self, methodName='runTest'):
169 """Create an instance of the class that will use the named test
170 method when executed. Raises a ValueError if the instance does
171 not have a method with the specified name.
172 """
173 self._testMethodName = methodName
174 self._resultForDoCleanups = None
175 try:
176 testMethod = getattr(self, methodName)
177 except AttributeError:
Michael Foordc2294dd2010-02-18 21:37:07 +0000178 raise ValueError("no such test method in %s: %s" %
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000179 (self.__class__, methodName))
180 self._testMethodDoc = testMethod.__doc__
181 self._cleanups = []
182
183 # Map types to custom assertEqual functions that will compare
184 # instances of said type in more detail to generate a more useful
185 # error message.
186 self._type_equality_funcs = {}
187 self.addTypeEqualityFunc(dict, self.assertDictEqual)
188 self.addTypeEqualityFunc(list, self.assertListEqual)
189 self.addTypeEqualityFunc(tuple, self.assertTupleEqual)
190 self.addTypeEqualityFunc(set, self.assertSetEqual)
191 self.addTypeEqualityFunc(frozenset, self.assertSetEqual)
Michael Foordfe6349c2010-02-08 22:41:16 +0000192 self.addTypeEqualityFunc(unicode, self.assertMultiLineEqual)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000193
194 def addTypeEqualityFunc(self, typeobj, function):
195 """Add a type specific assertEqual style function to compare a type.
196
197 This method is for use by TestCase subclasses that need to register
198 their own type equality functions to provide nicer error messages.
199
200 Args:
201 typeobj: The data type to call this function on when both values
202 are of the same type in assertEqual().
203 function: The callable taking two arguments and an optional
204 msg= argument that raises self.failureException with a
205 useful error message when the two arguments are not equal.
206 """
Benjamin Petersond46430b2009-11-29 22:26:26 +0000207 self._type_equality_funcs[typeobj] = function
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000208
209 def addCleanup(self, function, *args, **kwargs):
210 """Add a function, with arguments, to be called when the test is
211 completed. Functions added are called on a LIFO basis and are
212 called after tearDown on test failure or success.
213
214 Cleanup items are called even if setUp fails (unlike tearDown)."""
215 self._cleanups.append((function, args, kwargs))
216
217 def setUp(self):
218 "Hook method for setting up the test fixture before exercising it."
219 pass
220
221 def tearDown(self):
222 "Hook method for deconstructing the test fixture after testing it."
223 pass
224
Michael Foord5ffa3252010-03-07 22:04:55 +0000225 @classmethod
226 def setUpClass(cls):
227 "Hook method for setting up class fixture before running tests in the class."
228
229 @classmethod
230 def tearDownClass(cls):
231 "Hook method for deconstructing the class fixture after running all tests in the class."
232
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000233 def countTestCases(self):
234 return 1
235
236 def defaultTestResult(self):
237 return result.TestResult()
238
239 def shortDescription(self):
Michael Foorddb43b5a2010-02-10 14:25:12 +0000240 """Returns a one-line description of the test, or None if no
241 description has been provided.
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000242
Michael Foorddb43b5a2010-02-10 14:25:12 +0000243 The default implementation of this method returns the first line of
244 the specified test method's docstring.
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000245 """
Michael Foorddb43b5a2010-02-10 14:25:12 +0000246 doc = self._testMethodDoc
247 return doc and doc.split("\n")[0].strip() or None
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000248
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000249
250 def id(self):
Michael Foord225a0992010-02-18 20:30:09 +0000251 return "%s.%s" % (strclass(self.__class__), self._testMethodName)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000252
253 def __eq__(self, other):
254 if type(self) is not type(other):
255 return NotImplemented
256
257 return self._testMethodName == other._testMethodName
258
259 def __ne__(self, other):
260 return not self == other
261
262 def __hash__(self):
263 return hash((type(self), self._testMethodName))
264
265 def __str__(self):
Michael Foord225a0992010-02-18 20:30:09 +0000266 return "%s (%s)" % (self._testMethodName, strclass(self.__class__))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000267
268 def __repr__(self):
269 return "<%s testMethod=%s>" % \
Michael Foord225a0992010-02-18 20:30:09 +0000270 (strclass(self.__class__), self._testMethodName)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000271
Michael Foordae3db0a2010-02-22 23:28:32 +0000272 def _addSkip(self, result, reason):
273 addSkip = getattr(result, 'addSkip', None)
274 if addSkip is not None:
275 addSkip(self, reason)
276 else:
277 warnings.warn("TestResult has no addSkip method, skips not reported",
278 RuntimeWarning, 2)
279 result.addSuccess(self)
280
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000281 def run(self, result=None):
282 orig_result = result
283 if result is None:
284 result = self.defaultTestResult()
285 startTestRun = getattr(result, 'startTestRun', None)
286 if startTestRun is not None:
287 startTestRun()
288
289 self._resultForDoCleanups = result
290 result.startTest(self)
Michael Foord53e8eea2010-03-07 20:22:12 +0000291
292 testMethod = getattr(self, self._testMethodName)
293 if (getattr(self.__class__, "__unittest_skip__", False) or
294 getattr(testMethod, "__unittest_skip__", False)):
295 # If the class or method was skipped.
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000296 try:
Michael Foord53e8eea2010-03-07 20:22:12 +0000297 skip_why = (getattr(self.__class__, '__unittest_skip_why__', '')
298 or getattr(testMethod, '__unittest_skip_why__', ''))
299 self._addSkip(result, skip_why)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000300 finally:
301 result.stopTest(self)
302 return
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000303 try:
304 success = False
305 try:
306 self.setUp()
307 except SkipTest as e:
Michael Foordae3db0a2010-02-22 23:28:32 +0000308 self._addSkip(result, str(e))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000309 except Exception:
310 result.addError(self, sys.exc_info())
311 else:
312 try:
313 testMethod()
314 except self.failureException:
315 result.addFailure(self, sys.exc_info())
316 except _ExpectedFailure as e:
Michael Foordae3db0a2010-02-22 23:28:32 +0000317 addExpectedFailure = getattr(result, 'addExpectedFailure', None)
318 if addExpectedFailure is not None:
319 addExpectedFailure(self, e.exc_info)
320 else:
321 warnings.warn("TestResult has no addExpectedFailure method, reporting as passes",
322 RuntimeWarning)
323 result.addSuccess(self)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000324 except _UnexpectedSuccess:
Michael Foordae3db0a2010-02-22 23:28:32 +0000325 addUnexpectedSuccess = getattr(result, 'addUnexpectedSuccess', None)
326 if addUnexpectedSuccess is not None:
327 addUnexpectedSuccess(self)
328 else:
329 warnings.warn("TestResult has no addUnexpectedSuccess method, reporting as failures",
330 RuntimeWarning)
331 result.addFailure(self, sys.exc_info())
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000332 except SkipTest as e:
Michael Foordae3db0a2010-02-22 23:28:32 +0000333 self._addSkip(result, str(e))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000334 except Exception:
335 result.addError(self, sys.exc_info())
336 else:
337 success = True
338
339 try:
340 self.tearDown()
341 except Exception:
342 result.addError(self, sys.exc_info())
343 success = False
344
345 cleanUpSuccess = self.doCleanups()
346 success = success and cleanUpSuccess
347 if success:
348 result.addSuccess(self)
349 finally:
350 result.stopTest(self)
351 if orig_result is None:
352 stopTestRun = getattr(result, 'stopTestRun', None)
353 if stopTestRun is not None:
354 stopTestRun()
355
356 def doCleanups(self):
357 """Execute all cleanup functions. Normally called for you after
358 tearDown."""
359 result = self._resultForDoCleanups
360 ok = True
361 while self._cleanups:
362 function, args, kwargs = self._cleanups.pop(-1)
363 try:
364 function(*args, **kwargs)
365 except Exception:
366 ok = False
367 result.addError(self, sys.exc_info())
368 return ok
369
370 def __call__(self, *args, **kwds):
371 return self.run(*args, **kwds)
372
373 def debug(self):
374 """Run the test without collecting errors in a TestResult"""
375 self.setUp()
376 getattr(self, self._testMethodName)()
377 self.tearDown()
378
379 def skipTest(self, reason):
380 """Skip this test."""
381 raise SkipTest(reason)
382
383 def fail(self, msg=None):
384 """Fail immediately, with the given message."""
385 raise self.failureException(msg)
386
387 def assertFalse(self, expr, msg=None):
388 "Fail the test if the expression is true."
389 if expr:
Michael Foord225a0992010-02-18 20:30:09 +0000390 msg = self._formatMessage(msg, "%s is not False" % safe_repr(expr))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000391 raise self.failureException(msg)
392
393 def assertTrue(self, expr, msg=None):
394 """Fail the test unless the expression is true."""
395 if not expr:
Michael Foord225a0992010-02-18 20:30:09 +0000396 msg = self._formatMessage(msg, "%s is not True" % safe_repr(expr))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000397 raise self.failureException(msg)
398
399 def _formatMessage(self, msg, standardMsg):
400 """Honour the longMessage attribute when generating failure messages.
401 If longMessage is False this means:
402 * Use only an explicit message if it is provided
403 * Otherwise use the standard message for the assert
404
405 If longMessage is True:
406 * Use the standard message
407 * If an explicit message is provided, plus ' : ' and the explicit message
408 """
409 if not self.longMessage:
410 return msg or standardMsg
411 if msg is None:
412 return standardMsg
Michael Foord53e8eea2010-03-07 20:22:12 +0000413 try:
414 # don't switch to '{}' formatting in Python 2.X
415 # it changes the way unicode input is handled
416 return '%s : %s' % (standardMsg, msg)
417 except UnicodeDecodeError:
418 return '%s : %s' % (safe_repr(standardMsg), safe_repr(msg))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000419
420
421 def assertRaises(self, excClass, callableObj=None, *args, **kwargs):
422 """Fail unless an exception of class excClass is thrown
423 by callableObj when invoked with arguments args and keyword
424 arguments kwargs. If a different type of exception is
425 thrown, it will not be caught, and the test case will be
426 deemed to have suffered an error, exactly as for an
427 unexpected exception.
428
429 If called with callableObj omitted or None, will return a
430 context object used like this::
431
Michael Foordd0edec32010-02-05 22:55:09 +0000432 with self.assertRaises(SomeException):
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000433 do_something()
Michael Foordd0edec32010-02-05 22:55:09 +0000434
435 The context manager keeps a reference to the exception as
Ezio Melotticd4f6572010-02-08 21:52:08 +0000436 the 'exception' attribute. This allows you to inspect the
Michael Foordd0edec32010-02-05 22:55:09 +0000437 exception after the assertion::
438
439 with self.assertRaises(SomeException) as cm:
440 do_something()
Georg Brandldc3694b2010-02-07 17:02:22 +0000441 the_exception = cm.exception
Michael Foord757cc4d2010-02-05 23:22:37 +0000442 self.assertEqual(the_exception.error_code, 3)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000443 """
444 context = _AssertRaisesContext(excClass, self)
445 if callableObj is None:
446 return context
447 with context:
448 callableObj(*args, **kwargs)
449
450 def _getAssertEqualityFunc(self, first, second):
451 """Get a detailed comparison function for the types of the two args.
452
453 Returns: A callable accepting (first, second, msg=None) that will
454 raise a failure exception if first != second with a useful human
455 readable error message for those types.
456 """
457 #
458 # NOTE(gregory.p.smith): I considered isinstance(first, type(second))
459 # and vice versa. I opted for the conservative approach in case
460 # subclasses are not intended to be compared in detail to their super
461 # class instances using a type equality func. This means testing
462 # subtypes won't automagically use the detailed comparison. Callers
463 # should use their type specific assertSpamEqual method to compare
464 # subclasses if the detailed comparison is desired and appropriate.
465 # See the discussion in http://bugs.python.org/issue2578.
466 #
467 if type(first) is type(second):
468 asserter = self._type_equality_funcs.get(type(first))
469 if asserter is not None:
Benjamin Petersond46430b2009-11-29 22:26:26 +0000470 return asserter
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000471
472 return self._baseAssertEqual
473
474 def _baseAssertEqual(self, first, second, msg=None):
475 """The default assertEqual implementation, not type specific."""
476 if not first == second:
Michael Foord225a0992010-02-18 20:30:09 +0000477 standardMsg = '%s != %s' % (safe_repr(first), safe_repr(second))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000478 msg = self._formatMessage(msg, standardMsg)
479 raise self.failureException(msg)
480
481 def assertEqual(self, first, second, msg=None):
482 """Fail if the two objects are unequal as determined by the '=='
483 operator.
484 """
485 assertion_func = self._getAssertEqualityFunc(first, second)
486 assertion_func(first, second, msg=msg)
487
488 def assertNotEqual(self, first, second, msg=None):
489 """Fail if the two objects are equal as determined by the '=='
490 operator.
491 """
492 if not first != second:
Michael Foord225a0992010-02-18 20:30:09 +0000493 msg = self._formatMessage(msg, '%s == %s' % (safe_repr(first),
494 safe_repr(second)))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000495 raise self.failureException(msg)
496
Michael Foorda7e08fe2010-03-27 19:10:11 +0000497
498 def assertAlmostEqual(self, first, second, places=None, msg=None, delta=None):
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000499 """Fail if the two objects are unequal as determined by their
500 difference rounded to the given number of decimal places
Michael Foorda7e08fe2010-03-27 19:10:11 +0000501 (default 7) and comparing to zero, or by comparing that the
502 between the two objects is more than the given delta.
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000503
504 Note that decimal places (from zero) are usually not the same
505 as significant digits (measured from the most signficant digit).
Michael Foordc3f79372009-09-13 16:40:02 +0000506
507 If the two objects compare equal then they will automatically
508 compare almost equal.
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000509 """
Michael Foordc3f79372009-09-13 16:40:02 +0000510 if first == second:
Michael Foorda7e08fe2010-03-27 19:10:11 +0000511 # shortcut
Michael Foordc3f79372009-09-13 16:40:02 +0000512 return
Michael Foorda7e08fe2010-03-27 19:10:11 +0000513 if delta is not None and places is not None:
514 raise TypeError("specify delta or places not both")
515
516 if delta is not None:
517 if abs(first - second) <= delta:
518 return
519
520 standardMsg = '%s != %s within %s delta' % (safe_repr(first),
521 safe_repr(second),
522 safe_repr(delta))
523 else:
524 if places is None:
525 places = 7
526
527 if round(abs(second-first), places) == 0:
528 return
529
Michael Foord225a0992010-02-18 20:30:09 +0000530 standardMsg = '%s != %s within %r places' % (safe_repr(first),
531 safe_repr(second),
532 places)
Michael Foorda7e08fe2010-03-27 19:10:11 +0000533 msg = self._formatMessage(msg, standardMsg)
534 raise self.failureException(msg)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000535
Michael Foorda7e08fe2010-03-27 19:10:11 +0000536 def assertNotAlmostEqual(self, first, second, places=None, msg=None, delta=None):
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000537 """Fail if the two objects are equal as determined by their
538 difference rounded to the given number of decimal places
Michael Foorda7e08fe2010-03-27 19:10:11 +0000539 (default 7) and comparing to zero, or by comparing that the
540 between the two objects is less than the given delta.
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000541
542 Note that decimal places (from zero) are usually not the same
543 as significant digits (measured from the most signficant digit).
Michael Foordc3f79372009-09-13 16:40:02 +0000544
545 Objects that are equal automatically fail.
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000546 """
Michael Foorda7e08fe2010-03-27 19:10:11 +0000547 if delta is not None and places is not None:
548 raise TypeError("specify delta or places not both")
549 if delta is not None:
550 if not (first == second) and abs(first - second) > delta:
551 return
552 standardMsg = '%s == %s within %s delta' % (safe_repr(first),
553 safe_repr(second),
554 safe_repr(delta))
555 else:
556 if places is None:
557 places = 7
558 if not (first == second) and round(abs(second-first), places) != 0:
559 return
Michael Foord225a0992010-02-18 20:30:09 +0000560 standardMsg = '%s == %s within %r places' % (safe_repr(first),
Michael Foorda7e08fe2010-03-27 19:10:11 +0000561 safe_repr(second),
562 places)
563
564 msg = self._formatMessage(msg, standardMsg)
565 raise self.failureException(msg)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000566
567 # Synonyms for assertion methods
568
569 # The plurals are undocumented. Keep them that way to discourage use.
570 # Do not add more. Do not remove.
571 # Going through a deprecation cycle on these would annoy many people.
572 assertEquals = assertEqual
573 assertNotEquals = assertNotEqual
574 assertAlmostEquals = assertAlmostEqual
575 assertNotAlmostEquals = assertNotAlmostEqual
Michael Foord67dfc772010-02-10 14:31:30 +0000576 assert_ = assertTrue
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000577
578 # These fail* assertion method names are pending deprecation and will
579 # be a DeprecationWarning in 3.2; http://bugs.python.org/issue2578
580 def _deprecate(original_func):
581 def deprecated_func(*args, **kwargs):
582 warnings.warn(
583 'Please use {0} instead.'.format(original_func.__name__),
584 PendingDeprecationWarning, 2)
585 return original_func(*args, **kwargs)
586 return deprecated_func
587
588 failUnlessEqual = _deprecate(assertEqual)
589 failIfEqual = _deprecate(assertNotEqual)
590 failUnlessAlmostEqual = _deprecate(assertAlmostEqual)
591 failIfAlmostEqual = _deprecate(assertNotAlmostEqual)
592 failUnless = _deprecate(assertTrue)
593 failUnlessRaises = _deprecate(assertRaises)
594 failIf = _deprecate(assertFalse)
595
Michael Foorde37d75f2010-06-05 12:10:52 +0000596 def assertSequenceEqual(self, seq1, seq2, msg=None, seq_type=None):
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000597 """An equality assertion for ordered sequences (like lists and tuples).
598
R. David Murray05b41712010-01-29 19:35:39 +0000599 For the purposes of this function, a valid ordered sequence type is one
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000600 which can be indexed, has a length, and has an equality operator.
601
602 Args:
603 seq1: The first sequence to compare.
604 seq2: The second sequence to compare.
605 seq_type: The expected datatype of the sequences, or None if no
606 datatype should be enforced.
607 msg: Optional message to use on failure instead of a list of
608 differences.
609 """
Florent Xicluna4a0f8b82010-03-21 10:50:44 +0000610 if seq_type is not None:
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000611 seq_type_name = seq_type.__name__
612 if not isinstance(seq1, seq_type):
Michael Foord225a0992010-02-18 20:30:09 +0000613 raise self.failureException('First sequence is not a %s: %s'
614 % (seq_type_name, safe_repr(seq1)))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000615 if not isinstance(seq2, seq_type):
Michael Foord225a0992010-02-18 20:30:09 +0000616 raise self.failureException('Second sequence is not a %s: %s'
617 % (seq_type_name, safe_repr(seq2)))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000618 else:
619 seq_type_name = "sequence"
620
621 differing = None
622 try:
623 len1 = len(seq1)
624 except (TypeError, NotImplementedError):
625 differing = 'First %s has no length. Non-sequence?' % (
626 seq_type_name)
627
628 if differing is None:
629 try:
630 len2 = len(seq2)
631 except (TypeError, NotImplementedError):
632 differing = 'Second %s has no length. Non-sequence?' % (
633 seq_type_name)
634
635 if differing is None:
636 if seq1 == seq2:
637 return
638
Michael Foord225a0992010-02-18 20:30:09 +0000639 seq1_repr = safe_repr(seq1)
640 seq2_repr = safe_repr(seq2)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000641 if len(seq1_repr) > 30:
642 seq1_repr = seq1_repr[:30] + '...'
643 if len(seq2_repr) > 30:
644 seq2_repr = seq2_repr[:30] + '...'
645 elements = (seq_type_name.capitalize(), seq1_repr, seq2_repr)
646 differing = '%ss differ: %s != %s\n' % elements
647
648 for i in xrange(min(len1, len2)):
649 try:
650 item1 = seq1[i]
651 except (TypeError, IndexError, NotImplementedError):
652 differing += ('\nUnable to index element %d of first %s\n' %
653 (i, seq_type_name))
654 break
655
656 try:
657 item2 = seq2[i]
658 except (TypeError, IndexError, NotImplementedError):
659 differing += ('\nUnable to index element %d of second %s\n' %
660 (i, seq_type_name))
661 break
662
663 if item1 != item2:
664 differing += ('\nFirst differing element %d:\n%s\n%s\n' %
665 (i, item1, item2))
666 break
667 else:
668 if (len1 == len2 and seq_type is None and
669 type(seq1) != type(seq2)):
670 # The sequences are the same, but have differing types.
671 return
672
673 if len1 > len2:
674 differing += ('\nFirst %s contains %d additional '
675 'elements.\n' % (seq_type_name, len1 - len2))
676 try:
677 differing += ('First extra element %d:\n%s\n' %
678 (len2, seq1[len2]))
679 except (TypeError, IndexError, NotImplementedError):
680 differing += ('Unable to index element %d '
681 'of first %s\n' % (len2, seq_type_name))
682 elif len1 < len2:
683 differing += ('\nSecond %s contains %d additional '
684 'elements.\n' % (seq_type_name, len2 - len1))
685 try:
686 differing += ('First extra element %d:\n%s\n' %
687 (len1, seq2[len1]))
688 except (TypeError, IndexError, NotImplementedError):
689 differing += ('Unable to index element %d '
690 'of second %s\n' % (len1, seq_type_name))
Michael Foord01007022010-06-05 11:23:51 +0000691 standardMsg = differing
692 diffMsg = '\n' + '\n'.join(
Georg Brandl46cc46a2009-10-01 20:11:14 +0000693 difflib.ndiff(pprint.pformat(seq1).splitlines(),
694 pprint.pformat(seq2).splitlines()))
Michael Foorde37d75f2010-06-05 12:10:52 +0000695 standardMsg = self._truncateMessage(standardMsg, diffMsg)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000696 msg = self._formatMessage(msg, standardMsg)
697 self.fail(msg)
698
Michael Foorde37d75f2010-06-05 12:10:52 +0000699 def _truncateMessage(self, message, diff):
700 max_diff = self.maxDiff
Michael Foorda4412872010-06-05 11:46:59 +0000701 if max_diff is None or len(diff) <= max_diff:
702 return message + diff
Michael Foorde37d75f2010-06-05 12:10:52 +0000703 return message
Michael Foorda4412872010-06-05 11:46:59 +0000704
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000705 def assertListEqual(self, list1, list2, msg=None):
706 """A list-specific equality assertion.
707
708 Args:
709 list1: The first list to compare.
710 list2: The second list to compare.
711 msg: Optional message to use on failure instead of a list of
712 differences.
713
714 """
715 self.assertSequenceEqual(list1, list2, msg, seq_type=list)
716
717 def assertTupleEqual(self, tuple1, tuple2, msg=None):
718 """A tuple-specific equality assertion.
719
720 Args:
721 tuple1: The first tuple to compare.
722 tuple2: The second tuple to compare.
723 msg: Optional message to use on failure instead of a list of
724 differences.
725 """
726 self.assertSequenceEqual(tuple1, tuple2, msg, seq_type=tuple)
727
728 def assertSetEqual(self, set1, set2, msg=None):
729 """A set-specific equality assertion.
730
731 Args:
732 set1: The first set to compare.
733 set2: The second set to compare.
734 msg: Optional message to use on failure instead of a list of
735 differences.
736
Michael Foord98e7b762010-03-20 03:00:34 +0000737 assertSetEqual uses ducktyping to support different types of sets, and
738 is optimized for sets specifically (parameters must support a
739 difference method).
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000740 """
741 try:
742 difference1 = set1.difference(set2)
743 except TypeError, e:
744 self.fail('invalid type when attempting set difference: %s' % e)
745 except AttributeError, e:
746 self.fail('first argument does not support set difference: %s' % e)
747
748 try:
749 difference2 = set2.difference(set1)
750 except TypeError, e:
751 self.fail('invalid type when attempting set difference: %s' % e)
752 except AttributeError, e:
753 self.fail('second argument does not support set difference: %s' % e)
754
755 if not (difference1 or difference2):
756 return
757
758 lines = []
759 if difference1:
760 lines.append('Items in the first set but not the second:')
761 for item in difference1:
762 lines.append(repr(item))
763 if difference2:
764 lines.append('Items in the second set but not the first:')
765 for item in difference2:
766 lines.append(repr(item))
767
768 standardMsg = '\n'.join(lines)
769 self.fail(self._formatMessage(msg, standardMsg))
770
771 def assertIn(self, member, container, msg=None):
772 """Just like self.assertTrue(a in b), but with a nicer default message."""
773 if member not in container:
Michael Foord225a0992010-02-18 20:30:09 +0000774 standardMsg = '%s not found in %s' % (safe_repr(member),
775 safe_repr(container))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000776 self.fail(self._formatMessage(msg, standardMsg))
777
778 def assertNotIn(self, member, container, msg=None):
779 """Just like self.assertTrue(a not in b), but with a nicer default message."""
780 if member in container:
Michael Foord225a0992010-02-18 20:30:09 +0000781 standardMsg = '%s unexpectedly found in %s' % (safe_repr(member),
782 safe_repr(container))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000783 self.fail(self._formatMessage(msg, standardMsg))
784
785 def assertIs(self, expr1, expr2, msg=None):
786 """Just like self.assertTrue(a is b), but with a nicer default message."""
787 if expr1 is not expr2:
Michael Foord225a0992010-02-18 20:30:09 +0000788 standardMsg = '%s is not %s' % (safe_repr(expr1),
Michael Foordc2294dd2010-02-18 21:37:07 +0000789 safe_repr(expr2))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000790 self.fail(self._formatMessage(msg, standardMsg))
791
792 def assertIsNot(self, expr1, expr2, msg=None):
793 """Just like self.assertTrue(a is not b), but with a nicer default message."""
794 if expr1 is expr2:
Michael Foord225a0992010-02-18 20:30:09 +0000795 standardMsg = 'unexpectedly identical: %s' % (safe_repr(expr1),)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000796 self.fail(self._formatMessage(msg, standardMsg))
797
798 def assertDictEqual(self, d1, d2, msg=None):
799 self.assert_(isinstance(d1, dict), 'First argument is not a dictionary')
800 self.assert_(isinstance(d2, dict), 'Second argument is not a dictionary')
801
802 if d1 != d2:
Michael Foord674648e2010-06-05 12:58:39 +0000803 standardMsg = '%s != %s' % (safe_repr(d1, True), safe_repr(d2, True))
Michael Foorde37d75f2010-06-05 12:10:52 +0000804 diff = ('\n' + '\n'.join(difflib.ndiff(
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000805 pprint.pformat(d1).splitlines(),
806 pprint.pformat(d2).splitlines())))
Michael Foord674648e2010-06-05 12:58:39 +0000807 standardMsg = self._truncateMessage(standardMsg, diff)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000808 self.fail(self._formatMessage(msg, standardMsg))
809
810 def assertDictContainsSubset(self, expected, actual, msg=None):
811 """Checks whether actual is a superset of expected."""
812 missing = []
813 mismatched = []
814 for key, value in expected.iteritems():
815 if key not in actual:
816 missing.append(key)
817 elif value != actual[key]:
Georg Brandl46cc46a2009-10-01 20:11:14 +0000818 mismatched.append('%s, expected: %s, actual: %s' %
Michael Foordc2294dd2010-02-18 21:37:07 +0000819 (safe_repr(key), safe_repr(value),
820 safe_repr(actual[key])))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000821
822 if not (missing or mismatched):
823 return
824
825 standardMsg = ''
826 if missing:
Michael Foord225a0992010-02-18 20:30:09 +0000827 standardMsg = 'Missing: %s' % ','.join(safe_repr(m) for m in
828 missing)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000829 if mismatched:
830 if standardMsg:
831 standardMsg += '; '
832 standardMsg += 'Mismatched values: %s' % ','.join(mismatched)
833
834 self.fail(self._formatMessage(msg, standardMsg))
835
Michael Foord98e7b762010-03-20 03:00:34 +0000836 def assertItemsEqual(self, expected_seq, actual_seq, msg=None):
837 """An unordered sequence / set specific comparison. It asserts that
838 expected_seq and actual_seq contain the same elements. It is
839 the equivalent of::
840
841 self.assertEqual(sorted(expected_seq), sorted(actual_seq))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000842
843 Raises with an error message listing which elements of expected_seq
844 are missing from actual_seq and vice versa if any.
Michael Foordd0edec32010-02-05 22:55:09 +0000845
Michael Foord98e7b762010-03-20 03:00:34 +0000846 Asserts that each element has the same count in both sequences.
847 Example:
848 - [0, 1, 1] and [1, 0, 1] compare equal.
849 - [0, 0, 1] and [0, 1] compare unequal.
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000850 """
Florent Xicluna1f3b4e12010-03-07 12:14:25 +0000851 with warnings.catch_warnings():
852 if sys.py3kwarning:
853 # Silence Py3k warning raised during the sorting
Florent Xicluna4a0f8b82010-03-21 10:50:44 +0000854 for _msg in ["(code|dict|type) inequality comparisons",
Michael Foord98e7b762010-03-20 03:00:34 +0000855 "builtin_function_or_method order comparisons",
856 "comparing unequal types"]:
Michael Foorda7152552010-03-07 23:10:36 +0000857 warnings.filterwarnings("ignore", _msg, DeprecationWarning)
Florent Xicluna1f3b4e12010-03-07 12:14:25 +0000858 try:
Florent Xicluna1f3b4e12010-03-07 12:14:25 +0000859 expected = sorted(expected_seq)
860 actual = sorted(actual_seq)
Michael Foord98e7b762010-03-20 03:00:34 +0000861 except TypeError:
862 # Unsortable items (example: set(), complex(), ...)
863 expected = list(expected_seq)
864 actual = list(actual_seq)
865 missing, unexpected = unorderable_list_difference(
866 expected, actual, ignore_duplicate=False
867 )
868 else:
869 return self.assertSequenceEqual(expected, actual, msg=msg)
870
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000871 errors = []
872 if missing:
Michael Foord225a0992010-02-18 20:30:09 +0000873 errors.append('Expected, but missing:\n %s' %
Michael Foord98e7b762010-03-20 03:00:34 +0000874 safe_repr(missing))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000875 if unexpected:
Michael Foord225a0992010-02-18 20:30:09 +0000876 errors.append('Unexpected, but present:\n %s' %
Michael Foord98e7b762010-03-20 03:00:34 +0000877 safe_repr(unexpected))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000878 if errors:
879 standardMsg = '\n'.join(errors)
880 self.fail(self._formatMessage(msg, standardMsg))
881
882 def assertMultiLineEqual(self, first, second, msg=None):
883 """Assert that two multi-line strings are equal."""
884 self.assert_(isinstance(first, basestring), (
885 'First argument is not a string'))
886 self.assert_(isinstance(second, basestring), (
887 'Second argument is not a string'))
888
889 if first != second:
Michael Foord674648e2010-06-05 12:58:39 +0000890 standardMsg = '%s != %s' % (safe_repr(d1, True), safe_repr(d2, True))
Michael Foorde37d75f2010-06-05 12:10:52 +0000891 diff = '\n' + ''.join(difflib.ndiff(first.splitlines(True),
Georg Brandl46cc46a2009-10-01 20:11:14 +0000892 second.splitlines(True)))
Michael Foord674648e2010-06-05 12:58:39 +0000893 standardMsg = self._truncateMessage(standardMsg, diff)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000894 self.fail(self._formatMessage(msg, standardMsg))
895
896 def assertLess(self, a, b, msg=None):
897 """Just like self.assertTrue(a < b), but with a nicer default message."""
898 if not a < b:
Michael Foord225a0992010-02-18 20:30:09 +0000899 standardMsg = '%s not less than %s' % (safe_repr(a), safe_repr(b))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000900 self.fail(self._formatMessage(msg, standardMsg))
901
902 def assertLessEqual(self, a, b, msg=None):
903 """Just like self.assertTrue(a <= b), but with a nicer default message."""
904 if not a <= b:
Michael Foord225a0992010-02-18 20:30:09 +0000905 standardMsg = '%s not less than or equal to %s' % (safe_repr(a), safe_repr(b))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000906 self.fail(self._formatMessage(msg, standardMsg))
907
908 def assertGreater(self, a, b, msg=None):
909 """Just like self.assertTrue(a > b), but with a nicer default message."""
910 if not a > b:
Michael Foord225a0992010-02-18 20:30:09 +0000911 standardMsg = '%s not greater than %s' % (safe_repr(a), safe_repr(b))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000912 self.fail(self._formatMessage(msg, standardMsg))
913
914 def assertGreaterEqual(self, a, b, msg=None):
915 """Just like self.assertTrue(a >= b), but with a nicer default message."""
916 if not a >= b:
Michael Foord225a0992010-02-18 20:30:09 +0000917 standardMsg = '%s not greater than or equal to %s' % (safe_repr(a), safe_repr(b))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000918 self.fail(self._formatMessage(msg, standardMsg))
919
920 def assertIsNone(self, obj, msg=None):
921 """Same as self.assertTrue(obj is None), with a nicer default message."""
922 if obj is not None:
Michael Foord225a0992010-02-18 20:30:09 +0000923 standardMsg = '%s is not None' % (safe_repr(obj),)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000924 self.fail(self._formatMessage(msg, standardMsg))
925
926 def assertIsNotNone(self, obj, msg=None):
927 """Included for symmetry with assertIsNone."""
928 if obj is None:
929 standardMsg = 'unexpectedly None'
930 self.fail(self._formatMessage(msg, standardMsg))
931
Georg Brandlf895cf52009-10-01 20:59:31 +0000932 def assertIsInstance(self, obj, cls, msg=None):
933 """Same as self.assertTrue(isinstance(obj, cls)), with a nicer
934 default message."""
935 if not isinstance(obj, cls):
Michael Foord225a0992010-02-18 20:30:09 +0000936 standardMsg = '%s is not an instance of %r' % (safe_repr(obj), cls)
Georg Brandlf895cf52009-10-01 20:59:31 +0000937 self.fail(self._formatMessage(msg, standardMsg))
938
939 def assertNotIsInstance(self, obj, cls, msg=None):
940 """Included for symmetry with assertIsInstance."""
941 if isinstance(obj, cls):
Michael Foord225a0992010-02-18 20:30:09 +0000942 standardMsg = '%s is an instance of %r' % (safe_repr(obj), cls)
Georg Brandlf895cf52009-10-01 20:59:31 +0000943 self.fail(self._formatMessage(msg, standardMsg))
944
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000945 def assertRaisesRegexp(self, expected_exception, expected_regexp,
946 callable_obj=None, *args, **kwargs):
947 """Asserts that the message in a raised exception matches a regexp.
948
949 Args:
950 expected_exception: Exception class expected to be raised.
951 expected_regexp: Regexp (re pattern object or string) expected
952 to be found in error message.
953 callable_obj: Function to be called.
954 args: Extra args.
955 kwargs: Extra kwargs.
956 """
957 context = _AssertRaisesContext(expected_exception, self, expected_regexp)
958 if callable_obj is None:
959 return context
960 with context:
961 callable_obj(*args, **kwargs)
962
Georg Brandlb0eb4d32010-02-07 11:34:15 +0000963 def assertRegexpMatches(self, text, expected_regexp, msg=None):
Michael Foord959c16d2010-05-08 16:40:52 +0000964 """Fail the test unless the text matches the regular expression."""
Georg Brandlb0eb4d32010-02-07 11:34:15 +0000965 if isinstance(expected_regexp, basestring):
966 expected_regexp = re.compile(expected_regexp)
967 if not expected_regexp.search(text):
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000968 msg = msg or "Regexp didn't match"
Georg Brandlb0eb4d32010-02-07 11:34:15 +0000969 msg = '%s: %r not found in %r' % (msg, expected_regexp.pattern, text)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000970 raise self.failureException(msg)
971
Michael Foorda04c7a02010-04-02 22:55:59 +0000972 def assertNotRegexpMatches(self, text, unexpected_regexp, msg=None):
Michael Foord959c16d2010-05-08 16:40:52 +0000973 """Fail the test if the text matches the regular expression."""
Michael Foorda04c7a02010-04-02 22:55:59 +0000974 if isinstance(unexpected_regexp, basestring):
975 unexpected_regexp = re.compile(unexpected_regexp)
976 match = unexpected_regexp.search(text)
977 if match:
978 msg = msg or "Regexp matched"
979 msg = '%s: %r matches %r in %r' % (msg,
980 text[match.start():match.end()],
981 unexpected_regexp.pattern,
982 text)
983 raise self.failureException(msg)
984
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000985
986class FunctionTestCase(TestCase):
987 """A test case that wraps a test function.
988
989 This is useful for slipping pre-existing test functions into the
990 unittest framework. Optionally, set-up and tidy-up functions can be
991 supplied. As with TestCase, the tidy-up ('tearDown') function will
992 always be called if the set-up ('setUp') function ran successfully.
993 """
994
995 def __init__(self, testFunc, setUp=None, tearDown=None, description=None):
996 super(FunctionTestCase, self).__init__()
997 self._setUpFunc = setUp
998 self._tearDownFunc = tearDown
999 self._testFunc = testFunc
1000 self._description = description
1001
1002 def setUp(self):
1003 if self._setUpFunc is not None:
1004 self._setUpFunc()
1005
1006 def tearDown(self):
1007 if self._tearDownFunc is not None:
1008 self._tearDownFunc()
1009
1010 def runTest(self):
1011 self._testFunc()
1012
1013 def id(self):
1014 return self._testFunc.__name__
1015
1016 def __eq__(self, other):
1017 if not isinstance(other, self.__class__):
1018 return NotImplemented
1019
1020 return self._setUpFunc == other._setUpFunc and \
1021 self._tearDownFunc == other._tearDownFunc and \
1022 self._testFunc == other._testFunc and \
1023 self._description == other._description
1024
1025 def __ne__(self, other):
1026 return not self == other
1027
1028 def __hash__(self):
1029 return hash((type(self), self._setUpFunc, self._tearDownFunc,
1030 self._testFunc, self._description))
1031
1032 def __str__(self):
Michael Foord225a0992010-02-18 20:30:09 +00001033 return "%s (%s)" % (strclass(self.__class__),
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +00001034 self._testFunc.__name__)
1035
1036 def __repr__(self):
Michael Foord225a0992010-02-18 20:30:09 +00001037 return "<%s tec=%s>" % (strclass(self.__class__),
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +00001038 self._testFunc)
1039
1040 def shortDescription(self):
1041 if self._description is not None:
1042 return self._description
1043 doc = self._testFunc.__doc__
1044 return doc and doc.split("\n")[0].strip() or None