blob: 08e8c1e9640aab915b3f6c6e07cbdf75492d564b [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
Michael Foord5fe21ff2010-06-05 13:38:16 +000017
18DIFF_OMITTED = ('\nDiff is %s characters long. '
19 'Set self.maxDiff to None to see it.')
20
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000021class SkipTest(Exception):
22 """
23 Raise this exception in a test to skip it.
24
25 Usually you can use TestResult.skip() or one of the skipping decorators
26 instead of raising this directly.
27 """
28 pass
29
30class _ExpectedFailure(Exception):
31 """
32 Raise this when a test is expected to fail.
33
34 This is an implementation detail.
35 """
36
37 def __init__(self, exc_info):
38 super(_ExpectedFailure, self).__init__()
39 self.exc_info = exc_info
40
41class _UnexpectedSuccess(Exception):
42 """
43 The test was supposed to fail, but it didn't!
44 """
45 pass
46
47def _id(obj):
48 return obj
49
50def skip(reason):
51 """
52 Unconditionally skip a test.
53 """
54 def decorator(test_item):
Michael Foord53e8eea2010-03-07 20:22:12 +000055 if not (isinstance(test_item, type) and issubclass(test_item, TestCase)):
56 @functools.wraps(test_item)
57 def skip_wrapper(*args, **kwargs):
58 raise SkipTest(reason)
59 test_item = skip_wrapper
60
61 test_item.__unittest_skip__ = True
62 test_item.__unittest_skip_why__ = reason
63 return test_item
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000064 return decorator
65
66def skipIf(condition, reason):
67 """
68 Skip a test if the condition is true.
69 """
70 if condition:
71 return skip(reason)
72 return _id
73
74def skipUnless(condition, reason):
75 """
76 Skip a test unless the condition is true.
77 """
78 if not condition:
79 return skip(reason)
80 return _id
81
82
83def expectedFailure(func):
84 @functools.wraps(func)
85 def wrapper(*args, **kwargs):
86 try:
87 func(*args, **kwargs)
88 except Exception:
89 raise _ExpectedFailure(sys.exc_info())
90 raise _UnexpectedSuccess
91 return wrapper
92
93
94class _AssertRaisesContext(object):
95 """A context manager used to implement TestCase.assertRaises* methods."""
96
97 def __init__(self, expected, test_case, expected_regexp=None):
98 self.expected = expected
99 self.failureException = test_case.failureException
Georg Brandlb0eb4d32010-02-07 11:34:15 +0000100 self.expected_regexp = expected_regexp
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000101
102 def __enter__(self):
Michael Foord2bd52dc2010-02-07 18:44:12 +0000103 return self
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000104
105 def __exit__(self, exc_type, exc_value, tb):
106 if exc_type is None:
107 try:
108 exc_name = self.expected.__name__
109 except AttributeError:
110 exc_name = str(self.expected)
111 raise self.failureException(
112 "{0} not raised".format(exc_name))
113 if not issubclass(exc_type, self.expected):
114 # let unexpected exceptions pass through
115 return False
Georg Brandldc3694b2010-02-07 17:02:22 +0000116 self.exception = exc_value # store for later retrieval
Georg Brandlb0eb4d32010-02-07 11:34:15 +0000117 if self.expected_regexp is None:
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000118 return True
119
Georg Brandlb0eb4d32010-02-07 11:34:15 +0000120 expected_regexp = self.expected_regexp
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000121 if isinstance(expected_regexp, basestring):
122 expected_regexp = re.compile(expected_regexp)
123 if not expected_regexp.search(str(exc_value)):
124 raise self.failureException('"%s" does not match "%s"' %
125 (expected_regexp.pattern, str(exc_value)))
126 return True
127
128
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000129class TestCase(object):
130 """A class whose instances are single test cases.
131
132 By default, the test code itself should be placed in a method named
133 'runTest'.
134
135 If the fixture may be used for many test cases, create as
136 many test methods as are needed. When instantiating such a TestCase
137 subclass, specify in the constructor arguments the name of the test method
138 that the instance is to execute.
139
140 Test authors should subclass TestCase for their own tests. Construction
141 and deconstruction of the test's environment ('fixture') can be
142 implemented by overriding the 'setUp' and 'tearDown' methods respectively.
143
144 If it is necessary to override the __init__ method, the base class
145 __init__ method must always be called. It is important that subclasses
146 should not change the signature of their __init__ method, since instances
147 of the classes are instantiated automatically by parts of the framework
148 in order to be run.
149 """
150
151 # This attribute determines which exception will be raised when
152 # the instance's assertion methods fail; test methods raising this
153 # exception will be deemed to have 'failed' rather than 'errored'
154
155 failureException = AssertionError
156
157 # This attribute determines whether long messages (including repr of
158 # objects used in assert methods) will be printed on failure in *addition*
159 # to any explicit message passed.
160
161 longMessage = False
162
Michael Foorde37d75f2010-06-05 12:10:52 +0000163 # This attribute sets the maximum length of a diff in failure messsages
164 # by assert methods using difflib. It is looked up as an instance attribute
165 # so can be configured by individual tests if required.
166 maxDiff = 80*8
167
Michael Foord5ffa3252010-03-07 22:04:55 +0000168 # Attribute used by TestSuite for classSetUp
169
170 _classSetupFailed = False
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000171
172 def __init__(self, methodName='runTest'):
173 """Create an instance of the class that will use the named test
174 method when executed. Raises a ValueError if the instance does
175 not have a method with the specified name.
176 """
177 self._testMethodName = methodName
178 self._resultForDoCleanups = None
179 try:
180 testMethod = getattr(self, methodName)
181 except AttributeError:
Michael Foordc2294dd2010-02-18 21:37:07 +0000182 raise ValueError("no such test method in %s: %s" %
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000183 (self.__class__, methodName))
184 self._testMethodDoc = testMethod.__doc__
185 self._cleanups = []
186
187 # Map types to custom assertEqual functions that will compare
188 # instances of said type in more detail to generate a more useful
189 # error message.
190 self._type_equality_funcs = {}
191 self.addTypeEqualityFunc(dict, self.assertDictEqual)
192 self.addTypeEqualityFunc(list, self.assertListEqual)
193 self.addTypeEqualityFunc(tuple, self.assertTupleEqual)
194 self.addTypeEqualityFunc(set, self.assertSetEqual)
195 self.addTypeEqualityFunc(frozenset, self.assertSetEqual)
Michael Foordfe6349c2010-02-08 22:41:16 +0000196 self.addTypeEqualityFunc(unicode, self.assertMultiLineEqual)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000197
198 def addTypeEqualityFunc(self, typeobj, function):
199 """Add a type specific assertEqual style function to compare a type.
200
201 This method is for use by TestCase subclasses that need to register
202 their own type equality functions to provide nicer error messages.
203
204 Args:
205 typeobj: The data type to call this function on when both values
206 are of the same type in assertEqual().
207 function: The callable taking two arguments and an optional
208 msg= argument that raises self.failureException with a
209 useful error message when the two arguments are not equal.
210 """
Benjamin Petersond46430b2009-11-29 22:26:26 +0000211 self._type_equality_funcs[typeobj] = function
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000212
213 def addCleanup(self, function, *args, **kwargs):
214 """Add a function, with arguments, to be called when the test is
215 completed. Functions added are called on a LIFO basis and are
216 called after tearDown on test failure or success.
217
218 Cleanup items are called even if setUp fails (unlike tearDown)."""
219 self._cleanups.append((function, args, kwargs))
220
221 def setUp(self):
222 "Hook method for setting up the test fixture before exercising it."
223 pass
224
225 def tearDown(self):
226 "Hook method for deconstructing the test fixture after testing it."
227 pass
228
Michael Foord5ffa3252010-03-07 22:04:55 +0000229 @classmethod
230 def setUpClass(cls):
231 "Hook method for setting up class fixture before running tests in the class."
232
233 @classmethod
234 def tearDownClass(cls):
235 "Hook method for deconstructing the class fixture after running all tests in the class."
236
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000237 def countTestCases(self):
238 return 1
239
240 def defaultTestResult(self):
241 return result.TestResult()
242
243 def shortDescription(self):
Michael Foorddb43b5a2010-02-10 14:25:12 +0000244 """Returns a one-line description of the test, or None if no
245 description has been provided.
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000246
Michael Foorddb43b5a2010-02-10 14:25:12 +0000247 The default implementation of this method returns the first line of
248 the specified test method's docstring.
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000249 """
Michael Foorddb43b5a2010-02-10 14:25:12 +0000250 doc = self._testMethodDoc
251 return doc and doc.split("\n")[0].strip() or None
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000252
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000253
254 def id(self):
Michael Foord225a0992010-02-18 20:30:09 +0000255 return "%s.%s" % (strclass(self.__class__), self._testMethodName)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000256
257 def __eq__(self, other):
258 if type(self) is not type(other):
259 return NotImplemented
260
261 return self._testMethodName == other._testMethodName
262
263 def __ne__(self, other):
264 return not self == other
265
266 def __hash__(self):
267 return hash((type(self), self._testMethodName))
268
269 def __str__(self):
Michael Foord225a0992010-02-18 20:30:09 +0000270 return "%s (%s)" % (self._testMethodName, strclass(self.__class__))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000271
272 def __repr__(self):
273 return "<%s testMethod=%s>" % \
Michael Foord225a0992010-02-18 20:30:09 +0000274 (strclass(self.__class__), self._testMethodName)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000275
Michael Foordae3db0a2010-02-22 23:28:32 +0000276 def _addSkip(self, result, reason):
277 addSkip = getattr(result, 'addSkip', None)
278 if addSkip is not None:
279 addSkip(self, reason)
280 else:
281 warnings.warn("TestResult has no addSkip method, skips not reported",
282 RuntimeWarning, 2)
283 result.addSuccess(self)
284
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000285 def run(self, result=None):
286 orig_result = result
287 if result is None:
288 result = self.defaultTestResult()
289 startTestRun = getattr(result, 'startTestRun', None)
290 if startTestRun is not None:
291 startTestRun()
292
293 self._resultForDoCleanups = result
294 result.startTest(self)
Michael Foord53e8eea2010-03-07 20:22:12 +0000295
296 testMethod = getattr(self, self._testMethodName)
297 if (getattr(self.__class__, "__unittest_skip__", False) or
298 getattr(testMethod, "__unittest_skip__", False)):
299 # If the class or method was skipped.
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000300 try:
Michael Foord53e8eea2010-03-07 20:22:12 +0000301 skip_why = (getattr(self.__class__, '__unittest_skip_why__', '')
302 or getattr(testMethod, '__unittest_skip_why__', ''))
303 self._addSkip(result, skip_why)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000304 finally:
305 result.stopTest(self)
306 return
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000307 try:
308 success = False
309 try:
310 self.setUp()
311 except SkipTest as e:
Michael Foordae3db0a2010-02-22 23:28:32 +0000312 self._addSkip(result, str(e))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000313 except Exception:
314 result.addError(self, sys.exc_info())
315 else:
316 try:
317 testMethod()
318 except self.failureException:
319 result.addFailure(self, sys.exc_info())
320 except _ExpectedFailure as e:
Michael Foordae3db0a2010-02-22 23:28:32 +0000321 addExpectedFailure = getattr(result, 'addExpectedFailure', None)
322 if addExpectedFailure is not None:
323 addExpectedFailure(self, e.exc_info)
324 else:
325 warnings.warn("TestResult has no addExpectedFailure method, reporting as passes",
326 RuntimeWarning)
327 result.addSuccess(self)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000328 except _UnexpectedSuccess:
Michael Foordae3db0a2010-02-22 23:28:32 +0000329 addUnexpectedSuccess = getattr(result, 'addUnexpectedSuccess', None)
330 if addUnexpectedSuccess is not None:
331 addUnexpectedSuccess(self)
332 else:
333 warnings.warn("TestResult has no addUnexpectedSuccess method, reporting as failures",
334 RuntimeWarning)
335 result.addFailure(self, sys.exc_info())
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000336 except SkipTest as e:
Michael Foordae3db0a2010-02-22 23:28:32 +0000337 self._addSkip(result, str(e))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000338 except Exception:
339 result.addError(self, sys.exc_info())
340 else:
341 success = True
342
343 try:
344 self.tearDown()
345 except Exception:
346 result.addError(self, sys.exc_info())
347 success = False
348
349 cleanUpSuccess = self.doCleanups()
350 success = success and cleanUpSuccess
351 if success:
352 result.addSuccess(self)
353 finally:
354 result.stopTest(self)
355 if orig_result is None:
356 stopTestRun = getattr(result, 'stopTestRun', None)
357 if stopTestRun is not None:
358 stopTestRun()
359
360 def doCleanups(self):
361 """Execute all cleanup functions. Normally called for you after
362 tearDown."""
363 result = self._resultForDoCleanups
364 ok = True
365 while self._cleanups:
366 function, args, kwargs = self._cleanups.pop(-1)
367 try:
368 function(*args, **kwargs)
369 except Exception:
370 ok = False
371 result.addError(self, sys.exc_info())
372 return ok
373
374 def __call__(self, *args, **kwds):
375 return self.run(*args, **kwds)
376
377 def debug(self):
378 """Run the test without collecting errors in a TestResult"""
379 self.setUp()
380 getattr(self, self._testMethodName)()
381 self.tearDown()
382
383 def skipTest(self, reason):
384 """Skip this test."""
385 raise SkipTest(reason)
386
387 def fail(self, msg=None):
388 """Fail immediately, with the given message."""
389 raise self.failureException(msg)
390
391 def assertFalse(self, expr, msg=None):
392 "Fail the test if the expression is true."
393 if expr:
Michael Foord225a0992010-02-18 20:30:09 +0000394 msg = self._formatMessage(msg, "%s is not False" % safe_repr(expr))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000395 raise self.failureException(msg)
396
397 def assertTrue(self, expr, msg=None):
398 """Fail the test unless the expression is true."""
399 if not expr:
Michael Foord225a0992010-02-18 20:30:09 +0000400 msg = self._formatMessage(msg, "%s is not True" % safe_repr(expr))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000401 raise self.failureException(msg)
402
403 def _formatMessage(self, msg, standardMsg):
404 """Honour the longMessage attribute when generating failure messages.
405 If longMessage is False this means:
406 * Use only an explicit message if it is provided
407 * Otherwise use the standard message for the assert
408
409 If longMessage is True:
410 * Use the standard message
411 * If an explicit message is provided, plus ' : ' and the explicit message
412 """
413 if not self.longMessage:
414 return msg or standardMsg
415 if msg is None:
416 return standardMsg
Michael Foord53e8eea2010-03-07 20:22:12 +0000417 try:
418 # don't switch to '{}' formatting in Python 2.X
419 # it changes the way unicode input is handled
420 return '%s : %s' % (standardMsg, msg)
421 except UnicodeDecodeError:
422 return '%s : %s' % (safe_repr(standardMsg), safe_repr(msg))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000423
424
425 def assertRaises(self, excClass, callableObj=None, *args, **kwargs):
426 """Fail unless an exception of class excClass is thrown
427 by callableObj when invoked with arguments args and keyword
428 arguments kwargs. If a different type of exception is
429 thrown, it will not be caught, and the test case will be
430 deemed to have suffered an error, exactly as for an
431 unexpected exception.
432
433 If called with callableObj omitted or None, will return a
434 context object used like this::
435
Michael Foordd0edec32010-02-05 22:55:09 +0000436 with self.assertRaises(SomeException):
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000437 do_something()
Michael Foordd0edec32010-02-05 22:55:09 +0000438
439 The context manager keeps a reference to the exception as
Ezio Melotticd4f6572010-02-08 21:52:08 +0000440 the 'exception' attribute. This allows you to inspect the
Michael Foordd0edec32010-02-05 22:55:09 +0000441 exception after the assertion::
442
443 with self.assertRaises(SomeException) as cm:
444 do_something()
Georg Brandldc3694b2010-02-07 17:02:22 +0000445 the_exception = cm.exception
Michael Foord757cc4d2010-02-05 23:22:37 +0000446 self.assertEqual(the_exception.error_code, 3)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000447 """
448 context = _AssertRaisesContext(excClass, self)
449 if callableObj is None:
450 return context
451 with context:
452 callableObj(*args, **kwargs)
453
454 def _getAssertEqualityFunc(self, first, second):
455 """Get a detailed comparison function for the types of the two args.
456
457 Returns: A callable accepting (first, second, msg=None) that will
458 raise a failure exception if first != second with a useful human
459 readable error message for those types.
460 """
461 #
462 # NOTE(gregory.p.smith): I considered isinstance(first, type(second))
463 # and vice versa. I opted for the conservative approach in case
464 # subclasses are not intended to be compared in detail to their super
465 # class instances using a type equality func. This means testing
466 # subtypes won't automagically use the detailed comparison. Callers
467 # should use their type specific assertSpamEqual method to compare
468 # subclasses if the detailed comparison is desired and appropriate.
469 # See the discussion in http://bugs.python.org/issue2578.
470 #
471 if type(first) is type(second):
472 asserter = self._type_equality_funcs.get(type(first))
473 if asserter is not None:
Benjamin Petersond46430b2009-11-29 22:26:26 +0000474 return asserter
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000475
476 return self._baseAssertEqual
477
478 def _baseAssertEqual(self, first, second, msg=None):
479 """The default assertEqual implementation, not type specific."""
480 if not first == second:
Michael Foord225a0992010-02-18 20:30:09 +0000481 standardMsg = '%s != %s' % (safe_repr(first), safe_repr(second))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000482 msg = self._formatMessage(msg, standardMsg)
483 raise self.failureException(msg)
484
485 def assertEqual(self, first, second, msg=None):
486 """Fail if the two objects are unequal as determined by the '=='
487 operator.
488 """
489 assertion_func = self._getAssertEqualityFunc(first, second)
490 assertion_func(first, second, msg=msg)
491
492 def assertNotEqual(self, first, second, msg=None):
493 """Fail if the two objects are equal as determined by the '=='
494 operator.
495 """
496 if not first != second:
Michael Foord225a0992010-02-18 20:30:09 +0000497 msg = self._formatMessage(msg, '%s == %s' % (safe_repr(first),
498 safe_repr(second)))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000499 raise self.failureException(msg)
500
Michael Foorda7e08fe2010-03-27 19:10:11 +0000501
502 def assertAlmostEqual(self, first, second, places=None, msg=None, delta=None):
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000503 """Fail if the two objects are unequal as determined by their
504 difference rounded to the given number of decimal places
Michael Foorda7e08fe2010-03-27 19:10:11 +0000505 (default 7) and comparing to zero, or by comparing that the
506 between the two objects is more than the given delta.
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000507
508 Note that decimal places (from zero) are usually not the same
509 as significant digits (measured from the most signficant digit).
Michael Foordc3f79372009-09-13 16:40:02 +0000510
511 If the two objects compare equal then they will automatically
512 compare almost equal.
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000513 """
Michael Foordc3f79372009-09-13 16:40:02 +0000514 if first == second:
Michael Foorda7e08fe2010-03-27 19:10:11 +0000515 # shortcut
Michael Foordc3f79372009-09-13 16:40:02 +0000516 return
Michael Foorda7e08fe2010-03-27 19:10:11 +0000517 if delta is not None and places is not None:
518 raise TypeError("specify delta or places not both")
519
520 if delta is not None:
521 if abs(first - second) <= delta:
522 return
523
524 standardMsg = '%s != %s within %s delta' % (safe_repr(first),
525 safe_repr(second),
526 safe_repr(delta))
527 else:
528 if places is None:
529 places = 7
530
531 if round(abs(second-first), places) == 0:
532 return
533
Michael Foord225a0992010-02-18 20:30:09 +0000534 standardMsg = '%s != %s within %r places' % (safe_repr(first),
535 safe_repr(second),
536 places)
Michael Foorda7e08fe2010-03-27 19:10:11 +0000537 msg = self._formatMessage(msg, standardMsg)
538 raise self.failureException(msg)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000539
Michael Foorda7e08fe2010-03-27 19:10:11 +0000540 def assertNotAlmostEqual(self, first, second, places=None, msg=None, delta=None):
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000541 """Fail if the two objects are equal as determined by their
542 difference rounded to the given number of decimal places
Michael Foorda7e08fe2010-03-27 19:10:11 +0000543 (default 7) and comparing to zero, or by comparing that the
544 between the two objects is less than the given delta.
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000545
546 Note that decimal places (from zero) are usually not the same
547 as significant digits (measured from the most signficant digit).
Michael Foordc3f79372009-09-13 16:40:02 +0000548
549 Objects that are equal automatically fail.
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000550 """
Michael Foorda7e08fe2010-03-27 19:10:11 +0000551 if delta is not None and places is not None:
552 raise TypeError("specify delta or places not both")
553 if delta is not None:
554 if not (first == second) and abs(first - second) > delta:
555 return
556 standardMsg = '%s == %s within %s delta' % (safe_repr(first),
557 safe_repr(second),
558 safe_repr(delta))
559 else:
560 if places is None:
561 places = 7
562 if not (first == second) and round(abs(second-first), places) != 0:
563 return
Michael Foord225a0992010-02-18 20:30:09 +0000564 standardMsg = '%s == %s within %r places' % (safe_repr(first),
Michael Foorda7e08fe2010-03-27 19:10:11 +0000565 safe_repr(second),
566 places)
567
568 msg = self._formatMessage(msg, standardMsg)
569 raise self.failureException(msg)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000570
571 # Synonyms for assertion methods
572
573 # The plurals are undocumented. Keep them that way to discourage use.
574 # Do not add more. Do not remove.
575 # Going through a deprecation cycle on these would annoy many people.
576 assertEquals = assertEqual
577 assertNotEquals = assertNotEqual
578 assertAlmostEquals = assertAlmostEqual
579 assertNotAlmostEquals = assertNotAlmostEqual
Michael Foord67dfc772010-02-10 14:31:30 +0000580 assert_ = assertTrue
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000581
582 # These fail* assertion method names are pending deprecation and will
583 # be a DeprecationWarning in 3.2; http://bugs.python.org/issue2578
584 def _deprecate(original_func):
585 def deprecated_func(*args, **kwargs):
586 warnings.warn(
587 'Please use {0} instead.'.format(original_func.__name__),
588 PendingDeprecationWarning, 2)
589 return original_func(*args, **kwargs)
590 return deprecated_func
591
592 failUnlessEqual = _deprecate(assertEqual)
593 failIfEqual = _deprecate(assertNotEqual)
594 failUnlessAlmostEqual = _deprecate(assertAlmostEqual)
595 failIfAlmostEqual = _deprecate(assertNotAlmostEqual)
596 failUnless = _deprecate(assertTrue)
597 failUnlessRaises = _deprecate(assertRaises)
598 failIf = _deprecate(assertFalse)
599
Michael Foorde37d75f2010-06-05 12:10:52 +0000600 def assertSequenceEqual(self, seq1, seq2, msg=None, seq_type=None):
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000601 """An equality assertion for ordered sequences (like lists and tuples).
602
R. David Murray05b41712010-01-29 19:35:39 +0000603 For the purposes of this function, a valid ordered sequence type is one
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000604 which can be indexed, has a length, and has an equality operator.
605
606 Args:
607 seq1: The first sequence to compare.
608 seq2: The second sequence to compare.
609 seq_type: The expected datatype of the sequences, or None if no
610 datatype should be enforced.
611 msg: Optional message to use on failure instead of a list of
612 differences.
613 """
Florent Xicluna4a0f8b82010-03-21 10:50:44 +0000614 if seq_type is not None:
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000615 seq_type_name = seq_type.__name__
616 if not isinstance(seq1, seq_type):
Michael Foord225a0992010-02-18 20:30:09 +0000617 raise self.failureException('First sequence is not a %s: %s'
618 % (seq_type_name, safe_repr(seq1)))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000619 if not isinstance(seq2, seq_type):
Michael Foord225a0992010-02-18 20:30:09 +0000620 raise self.failureException('Second sequence is not a %s: %s'
621 % (seq_type_name, safe_repr(seq2)))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000622 else:
623 seq_type_name = "sequence"
624
625 differing = None
626 try:
627 len1 = len(seq1)
628 except (TypeError, NotImplementedError):
629 differing = 'First %s has no length. Non-sequence?' % (
630 seq_type_name)
631
632 if differing is None:
633 try:
634 len2 = len(seq2)
635 except (TypeError, NotImplementedError):
636 differing = 'Second %s has no length. Non-sequence?' % (
637 seq_type_name)
638
639 if differing is None:
640 if seq1 == seq2:
641 return
642
Michael Foord225a0992010-02-18 20:30:09 +0000643 seq1_repr = safe_repr(seq1)
644 seq2_repr = safe_repr(seq2)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000645 if len(seq1_repr) > 30:
646 seq1_repr = seq1_repr[:30] + '...'
647 if len(seq2_repr) > 30:
648 seq2_repr = seq2_repr[:30] + '...'
649 elements = (seq_type_name.capitalize(), seq1_repr, seq2_repr)
650 differing = '%ss differ: %s != %s\n' % elements
651
652 for i in xrange(min(len1, len2)):
653 try:
654 item1 = seq1[i]
655 except (TypeError, IndexError, NotImplementedError):
656 differing += ('\nUnable to index element %d of first %s\n' %
657 (i, seq_type_name))
658 break
659
660 try:
661 item2 = seq2[i]
662 except (TypeError, IndexError, NotImplementedError):
663 differing += ('\nUnable to index element %d of second %s\n' %
664 (i, seq_type_name))
665 break
666
667 if item1 != item2:
668 differing += ('\nFirst differing element %d:\n%s\n%s\n' %
669 (i, item1, item2))
670 break
671 else:
672 if (len1 == len2 and seq_type is None and
673 type(seq1) != type(seq2)):
674 # The sequences are the same, but have differing types.
675 return
676
677 if len1 > len2:
678 differing += ('\nFirst %s contains %d additional '
679 'elements.\n' % (seq_type_name, len1 - len2))
680 try:
681 differing += ('First extra element %d:\n%s\n' %
682 (len2, seq1[len2]))
683 except (TypeError, IndexError, NotImplementedError):
684 differing += ('Unable to index element %d '
685 'of first %s\n' % (len2, seq_type_name))
686 elif len1 < len2:
687 differing += ('\nSecond %s contains %d additional '
688 'elements.\n' % (seq_type_name, len2 - len1))
689 try:
690 differing += ('First extra element %d:\n%s\n' %
691 (len1, seq2[len1]))
692 except (TypeError, IndexError, NotImplementedError):
693 differing += ('Unable to index element %d '
694 'of second %s\n' % (len1, seq_type_name))
Michael Foord01007022010-06-05 11:23:51 +0000695 standardMsg = differing
696 diffMsg = '\n' + '\n'.join(
Georg Brandl46cc46a2009-10-01 20:11:14 +0000697 difflib.ndiff(pprint.pformat(seq1).splitlines(),
698 pprint.pformat(seq2).splitlines()))
Michael Foorde37d75f2010-06-05 12:10:52 +0000699 standardMsg = self._truncateMessage(standardMsg, diffMsg)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000700 msg = self._formatMessage(msg, standardMsg)
701 self.fail(msg)
702
Michael Foorde37d75f2010-06-05 12:10:52 +0000703 def _truncateMessage(self, message, diff):
704 max_diff = self.maxDiff
Michael Foorda4412872010-06-05 11:46:59 +0000705 if max_diff is None or len(diff) <= max_diff:
706 return message + diff
Michael Foord5fe21ff2010-06-05 13:38:16 +0000707 return message + (DIFF_OMITTED % len(diff))
Michael Foorda4412872010-06-05 11:46:59 +0000708
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000709 def assertListEqual(self, list1, list2, msg=None):
710 """A list-specific equality assertion.
711
712 Args:
713 list1: The first list to compare.
714 list2: The second list to compare.
715 msg: Optional message to use on failure instead of a list of
716 differences.
717
718 """
719 self.assertSequenceEqual(list1, list2, msg, seq_type=list)
720
721 def assertTupleEqual(self, tuple1, tuple2, msg=None):
722 """A tuple-specific equality assertion.
723
724 Args:
725 tuple1: The first tuple to compare.
726 tuple2: The second tuple to compare.
727 msg: Optional message to use on failure instead of a list of
728 differences.
729 """
730 self.assertSequenceEqual(tuple1, tuple2, msg, seq_type=tuple)
731
732 def assertSetEqual(self, set1, set2, msg=None):
733 """A set-specific equality assertion.
734
735 Args:
736 set1: The first set to compare.
737 set2: The second set to compare.
738 msg: Optional message to use on failure instead of a list of
739 differences.
740
Michael Foord98e7b762010-03-20 03:00:34 +0000741 assertSetEqual uses ducktyping to support different types of sets, and
742 is optimized for sets specifically (parameters must support a
743 difference method).
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000744 """
745 try:
746 difference1 = set1.difference(set2)
747 except TypeError, e:
748 self.fail('invalid type when attempting set difference: %s' % e)
749 except AttributeError, e:
750 self.fail('first argument does not support set difference: %s' % e)
751
752 try:
753 difference2 = set2.difference(set1)
754 except TypeError, e:
755 self.fail('invalid type when attempting set difference: %s' % e)
756 except AttributeError, e:
757 self.fail('second argument does not support set difference: %s' % e)
758
759 if not (difference1 or difference2):
760 return
761
762 lines = []
763 if difference1:
764 lines.append('Items in the first set but not the second:')
765 for item in difference1:
766 lines.append(repr(item))
767 if difference2:
768 lines.append('Items in the second set but not the first:')
769 for item in difference2:
770 lines.append(repr(item))
771
772 standardMsg = '\n'.join(lines)
773 self.fail(self._formatMessage(msg, standardMsg))
774
775 def assertIn(self, member, container, msg=None):
776 """Just like self.assertTrue(a in b), but with a nicer default message."""
777 if member not in container:
Michael Foord225a0992010-02-18 20:30:09 +0000778 standardMsg = '%s not found in %s' % (safe_repr(member),
779 safe_repr(container))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000780 self.fail(self._formatMessage(msg, standardMsg))
781
782 def assertNotIn(self, member, container, msg=None):
783 """Just like self.assertTrue(a not in b), but with a nicer default message."""
784 if member in container:
Michael Foord225a0992010-02-18 20:30:09 +0000785 standardMsg = '%s unexpectedly found in %s' % (safe_repr(member),
786 safe_repr(container))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000787 self.fail(self._formatMessage(msg, standardMsg))
788
789 def assertIs(self, expr1, expr2, msg=None):
790 """Just like self.assertTrue(a is b), but with a nicer default message."""
791 if expr1 is not expr2:
Michael Foord225a0992010-02-18 20:30:09 +0000792 standardMsg = '%s is not %s' % (safe_repr(expr1),
Michael Foordc2294dd2010-02-18 21:37:07 +0000793 safe_repr(expr2))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000794 self.fail(self._formatMessage(msg, standardMsg))
795
796 def assertIsNot(self, expr1, expr2, msg=None):
797 """Just like self.assertTrue(a is not b), but with a nicer default message."""
798 if expr1 is expr2:
Michael Foord225a0992010-02-18 20:30:09 +0000799 standardMsg = 'unexpectedly identical: %s' % (safe_repr(expr1),)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000800 self.fail(self._formatMessage(msg, standardMsg))
801
802 def assertDictEqual(self, d1, d2, msg=None):
803 self.assert_(isinstance(d1, dict), 'First argument is not a dictionary')
804 self.assert_(isinstance(d2, dict), 'Second argument is not a dictionary')
805
806 if d1 != d2:
Michael Foord674648e2010-06-05 12:58:39 +0000807 standardMsg = '%s != %s' % (safe_repr(d1, True), safe_repr(d2, True))
Michael Foorde37d75f2010-06-05 12:10:52 +0000808 diff = ('\n' + '\n'.join(difflib.ndiff(
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000809 pprint.pformat(d1).splitlines(),
810 pprint.pformat(d2).splitlines())))
Michael Foord674648e2010-06-05 12:58:39 +0000811 standardMsg = self._truncateMessage(standardMsg, diff)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000812 self.fail(self._formatMessage(msg, standardMsg))
813
814 def assertDictContainsSubset(self, expected, actual, msg=None):
815 """Checks whether actual is a superset of expected."""
816 missing = []
817 mismatched = []
818 for key, value in expected.iteritems():
819 if key not in actual:
820 missing.append(key)
821 elif value != actual[key]:
Georg Brandl46cc46a2009-10-01 20:11:14 +0000822 mismatched.append('%s, expected: %s, actual: %s' %
Michael Foordc2294dd2010-02-18 21:37:07 +0000823 (safe_repr(key), safe_repr(value),
824 safe_repr(actual[key])))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000825
826 if not (missing or mismatched):
827 return
828
829 standardMsg = ''
830 if missing:
Michael Foord225a0992010-02-18 20:30:09 +0000831 standardMsg = 'Missing: %s' % ','.join(safe_repr(m) for m in
832 missing)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000833 if mismatched:
834 if standardMsg:
835 standardMsg += '; '
836 standardMsg += 'Mismatched values: %s' % ','.join(mismatched)
837
838 self.fail(self._formatMessage(msg, standardMsg))
839
Michael Foord98e7b762010-03-20 03:00:34 +0000840 def assertItemsEqual(self, expected_seq, actual_seq, msg=None):
841 """An unordered sequence / set specific comparison. It asserts that
842 expected_seq and actual_seq contain the same elements. It is
843 the equivalent of::
844
845 self.assertEqual(sorted(expected_seq), sorted(actual_seq))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000846
847 Raises with an error message listing which elements of expected_seq
848 are missing from actual_seq and vice versa if any.
Michael Foordd0edec32010-02-05 22:55:09 +0000849
Michael Foord98e7b762010-03-20 03:00:34 +0000850 Asserts that each element has the same count in both sequences.
851 Example:
852 - [0, 1, 1] and [1, 0, 1] compare equal.
853 - [0, 0, 1] and [0, 1] compare unequal.
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000854 """
Florent Xicluna1f3b4e12010-03-07 12:14:25 +0000855 with warnings.catch_warnings():
856 if sys.py3kwarning:
857 # Silence Py3k warning raised during the sorting
Florent Xicluna4a0f8b82010-03-21 10:50:44 +0000858 for _msg in ["(code|dict|type) inequality comparisons",
Michael Foord98e7b762010-03-20 03:00:34 +0000859 "builtin_function_or_method order comparisons",
860 "comparing unequal types"]:
Michael Foorda7152552010-03-07 23:10:36 +0000861 warnings.filterwarnings("ignore", _msg, DeprecationWarning)
Florent Xicluna1f3b4e12010-03-07 12:14:25 +0000862 try:
Florent Xicluna1f3b4e12010-03-07 12:14:25 +0000863 expected = sorted(expected_seq)
864 actual = sorted(actual_seq)
Michael Foord98e7b762010-03-20 03:00:34 +0000865 except TypeError:
866 # Unsortable items (example: set(), complex(), ...)
867 expected = list(expected_seq)
868 actual = list(actual_seq)
869 missing, unexpected = unorderable_list_difference(
870 expected, actual, ignore_duplicate=False
871 )
872 else:
873 return self.assertSequenceEqual(expected, actual, msg=msg)
874
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000875 errors = []
876 if missing:
Michael Foord225a0992010-02-18 20:30:09 +0000877 errors.append('Expected, but missing:\n %s' %
Michael Foord98e7b762010-03-20 03:00:34 +0000878 safe_repr(missing))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000879 if unexpected:
Michael Foord225a0992010-02-18 20:30:09 +0000880 errors.append('Unexpected, but present:\n %s' %
Michael Foord98e7b762010-03-20 03:00:34 +0000881 safe_repr(unexpected))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000882 if errors:
883 standardMsg = '\n'.join(errors)
884 self.fail(self._formatMessage(msg, standardMsg))
885
886 def assertMultiLineEqual(self, first, second, msg=None):
887 """Assert that two multi-line strings are equal."""
888 self.assert_(isinstance(first, basestring), (
889 'First argument is not a string'))
890 self.assert_(isinstance(second, basestring), (
891 'Second argument is not a string'))
892
893 if first != second:
Michael Foord674648e2010-06-05 12:58:39 +0000894 standardMsg = '%s != %s' % (safe_repr(d1, True), safe_repr(d2, True))
Michael Foorde37d75f2010-06-05 12:10:52 +0000895 diff = '\n' + ''.join(difflib.ndiff(first.splitlines(True),
Georg Brandl46cc46a2009-10-01 20:11:14 +0000896 second.splitlines(True)))
Michael Foord674648e2010-06-05 12:58:39 +0000897 standardMsg = self._truncateMessage(standardMsg, diff)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000898 self.fail(self._formatMessage(msg, standardMsg))
899
900 def assertLess(self, a, b, msg=None):
901 """Just like self.assertTrue(a < b), but with a nicer default message."""
902 if not a < b:
Michael Foord225a0992010-02-18 20:30:09 +0000903 standardMsg = '%s not less than %s' % (safe_repr(a), safe_repr(b))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000904 self.fail(self._formatMessage(msg, standardMsg))
905
906 def assertLessEqual(self, a, b, msg=None):
907 """Just like self.assertTrue(a <= b), but with a nicer default message."""
908 if not a <= b:
Michael Foord225a0992010-02-18 20:30:09 +0000909 standardMsg = '%s not less than or equal to %s' % (safe_repr(a), safe_repr(b))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000910 self.fail(self._formatMessage(msg, standardMsg))
911
912 def assertGreater(self, a, b, msg=None):
913 """Just like self.assertTrue(a > b), but with a nicer default message."""
914 if not a > b:
Michael Foord225a0992010-02-18 20:30:09 +0000915 standardMsg = '%s not greater than %s' % (safe_repr(a), safe_repr(b))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000916 self.fail(self._formatMessage(msg, standardMsg))
917
918 def assertGreaterEqual(self, a, b, msg=None):
919 """Just like self.assertTrue(a >= b), but with a nicer default message."""
920 if not a >= b:
Michael Foord225a0992010-02-18 20:30:09 +0000921 standardMsg = '%s not greater than or equal to %s' % (safe_repr(a), safe_repr(b))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000922 self.fail(self._formatMessage(msg, standardMsg))
923
924 def assertIsNone(self, obj, msg=None):
925 """Same as self.assertTrue(obj is None), with a nicer default message."""
926 if obj is not None:
Michael Foord225a0992010-02-18 20:30:09 +0000927 standardMsg = '%s is not None' % (safe_repr(obj),)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000928 self.fail(self._formatMessage(msg, standardMsg))
929
930 def assertIsNotNone(self, obj, msg=None):
931 """Included for symmetry with assertIsNone."""
932 if obj is None:
933 standardMsg = 'unexpectedly None'
934 self.fail(self._formatMessage(msg, standardMsg))
935
Georg Brandlf895cf52009-10-01 20:59:31 +0000936 def assertIsInstance(self, obj, cls, msg=None):
937 """Same as self.assertTrue(isinstance(obj, cls)), with a nicer
938 default message."""
939 if not isinstance(obj, cls):
Michael Foord225a0992010-02-18 20:30:09 +0000940 standardMsg = '%s is not an instance of %r' % (safe_repr(obj), cls)
Georg Brandlf895cf52009-10-01 20:59:31 +0000941 self.fail(self._formatMessage(msg, standardMsg))
942
943 def assertNotIsInstance(self, obj, cls, msg=None):
944 """Included for symmetry with assertIsInstance."""
945 if isinstance(obj, cls):
Michael Foord225a0992010-02-18 20:30:09 +0000946 standardMsg = '%s is an instance of %r' % (safe_repr(obj), cls)
Georg Brandlf895cf52009-10-01 20:59:31 +0000947 self.fail(self._formatMessage(msg, standardMsg))
948
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000949 def assertRaisesRegexp(self, expected_exception, expected_regexp,
950 callable_obj=None, *args, **kwargs):
951 """Asserts that the message in a raised exception matches a regexp.
952
953 Args:
954 expected_exception: Exception class expected to be raised.
955 expected_regexp: Regexp (re pattern object or string) expected
956 to be found in error message.
957 callable_obj: Function to be called.
958 args: Extra args.
959 kwargs: Extra kwargs.
960 """
961 context = _AssertRaisesContext(expected_exception, self, expected_regexp)
962 if callable_obj is None:
963 return context
964 with context:
965 callable_obj(*args, **kwargs)
966
Georg Brandlb0eb4d32010-02-07 11:34:15 +0000967 def assertRegexpMatches(self, text, expected_regexp, msg=None):
Michael Foord959c16d2010-05-08 16:40:52 +0000968 """Fail the test unless the text matches the regular expression."""
Georg Brandlb0eb4d32010-02-07 11:34:15 +0000969 if isinstance(expected_regexp, basestring):
970 expected_regexp = re.compile(expected_regexp)
971 if not expected_regexp.search(text):
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000972 msg = msg or "Regexp didn't match"
Georg Brandlb0eb4d32010-02-07 11:34:15 +0000973 msg = '%s: %r not found in %r' % (msg, expected_regexp.pattern, text)
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000974 raise self.failureException(msg)
975
Michael Foorda04c7a02010-04-02 22:55:59 +0000976 def assertNotRegexpMatches(self, text, unexpected_regexp, msg=None):
Michael Foord959c16d2010-05-08 16:40:52 +0000977 """Fail the test if the text matches the regular expression."""
Michael Foorda04c7a02010-04-02 22:55:59 +0000978 if isinstance(unexpected_regexp, basestring):
979 unexpected_regexp = re.compile(unexpected_regexp)
980 match = unexpected_regexp.search(text)
981 if match:
982 msg = msg or "Regexp matched"
983 msg = '%s: %r matches %r in %r' % (msg,
984 text[match.start():match.end()],
985 unexpected_regexp.pattern,
986 text)
987 raise self.failureException(msg)
988
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000989
990class FunctionTestCase(TestCase):
991 """A test case that wraps a test function.
992
993 This is useful for slipping pre-existing test functions into the
994 unittest framework. Optionally, set-up and tidy-up functions can be
995 supplied. As with TestCase, the tidy-up ('tearDown') function will
996 always be called if the set-up ('setUp') function ran successfully.
997 """
998
999 def __init__(self, testFunc, setUp=None, tearDown=None, description=None):
1000 super(FunctionTestCase, self).__init__()
1001 self._setUpFunc = setUp
1002 self._tearDownFunc = tearDown
1003 self._testFunc = testFunc
1004 self._description = description
1005
1006 def setUp(self):
1007 if self._setUpFunc is not None:
1008 self._setUpFunc()
1009
1010 def tearDown(self):
1011 if self._tearDownFunc is not None:
1012 self._tearDownFunc()
1013
1014 def runTest(self):
1015 self._testFunc()
1016
1017 def id(self):
1018 return self._testFunc.__name__
1019
1020 def __eq__(self, other):
1021 if not isinstance(other, self.__class__):
1022 return NotImplemented
1023
1024 return self._setUpFunc == other._setUpFunc and \
1025 self._tearDownFunc == other._tearDownFunc and \
1026 self._testFunc == other._testFunc and \
1027 self._description == other._description
1028
1029 def __ne__(self, other):
1030 return not self == other
1031
1032 def __hash__(self):
1033 return hash((type(self), self._setUpFunc, self._tearDownFunc,
1034 self._testFunc, self._description))
1035
1036 def __str__(self):
Michael Foord225a0992010-02-18 20:30:09 +00001037 return "%s (%s)" % (strclass(self.__class__),
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +00001038 self._testFunc.__name__)
1039
1040 def __repr__(self):
Michael Foord225a0992010-02-18 20:30:09 +00001041 return "<%s tec=%s>" % (strclass(self.__class__),
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +00001042 self._testFunc)
1043
1044 def shortDescription(self):
1045 if self._description is not None:
1046 return self._description
1047 doc = self._testFunc.__doc__
1048 return doc and doc.split("\n")[0].strip() or None