1 # Copyright (c) 2008-2011 testtools developers. See LICENSE for details.
3 """Test case related stuff."""
7 'clone_test_with_new_id',
23 from testtools
import (
27 from testtools
.compat
import (
31 from testtools
.matchers
import (
43 from testtools
.monkey
import patch
44 from testtools
.runtest
import RunTest
45 from testtools
.testresult
import TestResult
47 wraps
= try_import('functools.wraps')
49 class TestSkipped(Exception):
50 """Raised within TestCase.run() when a test is skipped."""
51 testSkipped
= try_import('unittest2.case.SkipTest', TestSkipped
)
52 TestSkipped
= try_import('unittest.case.SkipTest', TestSkipped
)
55 class _UnexpectedSuccess(Exception):
56 """An unexpected success was raised.
58 Note that this exception is private plumbing in testtools' testcase
61 _UnexpectedSuccess
= try_import(
62 'unittest2.case._UnexpectedSuccess', _UnexpectedSuccess
)
63 _UnexpectedSuccess
= try_import(
64 'unittest.case._UnexpectedSuccess', _UnexpectedSuccess
)
66 class _ExpectedFailure(Exception):
67 """An expected failure occured.
69 Note that this exception is private plumbing in testtools' testcase
72 _ExpectedFailure
= try_import(
73 'unittest2.case._ExpectedFailure', _ExpectedFailure
)
74 _ExpectedFailure
= try_import(
75 'unittest.case._ExpectedFailure', _ExpectedFailure
)
78 def run_test_with(test_runner
, **kwargs
):
79 """Decorate a test as using a specific ``RunTest``.
83 @run_test_with(CustomRunner, timeout=42)
87 The returned decorator works by setting an attribute on the decorated
88 function. `TestCase.__init__` looks for this attribute when deciding on a
89 ``RunTest`` factory. If you wish to use multiple decorators on a test
90 method, then you must either make this one the top-most decorator, or you
91 must write your decorators so that they update the wrapping function with
92 the attributes of the wrapped function. The latter is recommended style
93 anyway. ``functools.wraps``, ``functools.wrapper`` and
94 ``twisted.python.util.mergeFunctionMetadata`` can help you do this.
96 :param test_runner: A ``RunTest`` factory that takes a test case and an
97 optional list of exception handlers. See ``RunTest``.
98 :param kwargs: Keyword arguments to pass on as extra arguments to
100 :return: A decorator to be used for marking a test as needing a special
103 def decorator(function
):
104 # Set an attribute on 'function' which will inform TestCase how to
106 function
._run
_test
_with
= (
107 lambda case
, handlers
=None:
108 test_runner(case
, handlers
=handlers
, **kwargs
))
113 def _copy_content(content_object
):
114 """Make a copy of the given content object.
116 The content within ``content_object`` is iterated and saved. This is
117 useful when the source of the content is volatile, a log file in a
118 temporary directory for example.
120 :param content_object: A `content.Content` instance.
121 :return: A `content.Content` instance with the same mime-type as
122 ``content_object`` and a non-volatile copy of its content.
124 content_bytes
= list(content_object
.iter_bytes())
125 content_callback
= lambda: content_bytes
126 return content
.Content(content_object
.content_type
, content_callback
)
129 def gather_details(source_dict
, target_dict
):
130 """Merge the details from ``source_dict`` into ``target_dict``.
132 :param source_dict: A dictionary of details will be gathered.
133 :param target_dict: A dictionary into which details will be gathered.
135 for name
, content_object
in source_dict
.items():
137 disambiguator
= itertools
.count(1)
138 while new_name
in target_dict
:
139 new_name
= '%s-%d' % (name
, advance_iterator(disambiguator
))
141 target_dict
[name
] = _copy_content(content_object
)
144 class TestCase(unittest
.TestCase
):
145 """Extensions to the basic TestCase.
147 :ivar exception_handlers: Exceptions to catch from setUp, runTest and
148 tearDown. This list is able to be modified at any time and consists of
149 (exception_class, handler(case, result, exception_value)) pairs.
150 :cvar run_tests_with: A factory to make the ``RunTest`` to run tests with.
151 Defaults to ``RunTest``. The factory is expected to take a test case
152 and an optional list of exception handlers.
155 skipException
= TestSkipped
157 run_tests_with
= RunTest
159 def __init__(self
, *args
, **kwargs
):
160 """Construct a TestCase.
162 :param testMethod: The name of the method to run.
163 :keyword runTest: Optional class to use to execute the test. If not
164 supplied ``RunTest`` is used. The instance to be used is created
165 when run() is invoked, so will be fresh each time. Overrides
166 ``TestCase.run_tests_with`` if given.
168 runTest
= kwargs
.pop('runTest', None)
169 super(TestCase
, self
).__init
__(*args
, **kwargs
)
171 self
._unique
_id
_gen
= itertools
.count(1)
172 # Generators to ensure unique traceback ids. Maps traceback label to
174 self
._traceback
_id
_gens
= {}
175 self
.__setup
_called
= False
176 self
.__teardown
_called
= False
177 # __details is lazy-initialized so that a constructed-but-not-run
178 # TestCase is safe to use with clone_test_with_new_id.
179 self
.__details
= None
180 test_method
= self
._get
_test
_method
()
183 test_method
, '_run_test_with', self
.run_tests_with
)
184 self
.__RunTest
= runTest
185 self
.__exception
_handlers
= []
186 self
.exception_handlers
= [
187 (self
.skipException
, self
._report
_skip
),
188 (self
.failureException
, self
._report
_failure
),
189 (_ExpectedFailure
, self
._report
_expected
_failure
),
190 (_UnexpectedSuccess
, self
._report
_unexpected
_success
),
191 (Exception, self
._report
_error
),
193 if sys
.version_info
< (2, 6):
194 # Catch old-style string exceptions with None as the instance
195 self
.exception_handlers
.append((type(None), self
._report
_error
))
197 def __eq__(self
, other
):
198 eq
= getattr(unittest
.TestCase
, '__eq__', None)
199 if eq
is not None and not unittest
.TestCase
.__eq
__(self
, other
):
201 return self
.__dict
__ == other
.__dict
__
204 # We add id to the repr because it makes testing testtools easier.
205 return "<%s id=0x%0x>" % (self
.id(), id(self
))
207 def addDetail(self
, name
, content_object
):
208 """Add a detail to be reported with this test's outcome.
210 For more details see pydoc testtools.TestResult.
212 :param name: The name to give this detail.
213 :param content_object: The content object for this detail. See
214 testtools.content for more detail.
216 if self
.__details
is None:
218 self
.__details
[name
] = content_object
220 def getDetails(self
):
221 """Get the details dict that will be reported with this test's outcome.
223 For more details see pydoc testtools.TestResult.
225 if self
.__details
is None:
227 return self
.__details
229 def patch(self
, obj
, attribute
, value
):
230 """Monkey-patch 'obj.attribute' to 'value' while the test is running.
232 If 'obj' has no attribute, then the monkey-patch will still go ahead,
233 and the attribute will be deleted instead of restored to its original
236 :param obj: The object to patch. Can be anything.
237 :param attribute: The attribute on 'obj' to patch.
238 :param value: The value to set 'obj.attribute' to.
240 self
.addCleanup(patch(obj
, attribute
, value
))
242 def shortDescription(self
):
245 def skipTest(self
, reason
):
246 """Cause this test to be skipped.
248 This raises self.skipException(reason). skipException is raised
249 to permit a skip to be triggered at any point (during setUp or the
250 testMethod itself). The run() method catches skipException and
251 translates that into a call to the result objects addSkip method.
253 :param reason: The reason why the test is being skipped. This must
254 support being cast into a unicode string for reporting.
256 raise self
.skipException(reason
)
258 # skipTest is how python2.7 spells this. Sometime in the future
259 # This should be given a deprecation decorator - RBC 20100611.
262 def _formatTypes(self
, classOrIterable
):
263 """Format a class or a bunch of classes for display in an error."""
264 className
= getattr(classOrIterable
, '__name__', None)
265 if className
is None:
266 className
= ', '.join(klass
.__name
__ for klass
in classOrIterable
)
269 def addCleanup(self
, function
, *arguments
, **keywordArguments
):
270 """Add a cleanup function to be called after tearDown.
272 Functions added with addCleanup will be called in reverse order of
273 adding after tearDown, or after setUp if setUp raises an exception.
275 If a function added with addCleanup raises an exception, the error
276 will be recorded as a test error, and the next cleanup will then be
279 Cleanup functions are always called before a test finishes running,
280 even if setUp is aborted by an exception.
282 self
._cleanups
.append((function
, arguments
, keywordArguments
))
284 def addOnException(self
, handler
):
285 """Add a handler to be called when an exception occurs in test code.
287 This handler cannot affect what result methods are called, and is
288 called before any outcome is called on the result object. An example
289 use for it is to add some diagnostic state to the test details dict
290 which is expensive to calculate and not interesting for reporting in
293 Handlers are called before the outcome (such as addFailure) that
294 the exception has caused.
296 Handlers are called in first-added, first-called order, and if they
297 raise an exception, that will propogate out of the test running
298 machinery, halting test processing. As a result, do not call code that
299 may unreasonably fail.
301 self
.__exception
_handlers
.append(handler
)
303 def _add_reason(self
, reason
):
304 self
.addDetail('reason', content
.Content(
305 content
.ContentType('text', 'plain'),
306 lambda: [reason
.encode('utf8')]))
308 def assertEqual(self
, expected
, observed
, message
=''):
309 """Assert that 'expected' is equal to 'observed'.
311 :param expected: The expected value.
312 :param observed: The observed value.
313 :param message: An optional message to include in the error.
315 matcher
= Equals(expected
)
316 self
.assertThat(observed
, matcher
, message
)
318 failUnlessEqual
= assertEquals
= assertEqual
320 def assertIn(self
, needle
, haystack
):
321 """Assert that needle is in haystack."""
322 self
.assertThat(haystack
, Contains(needle
))
324 def assertIsNone(self
, observed
, message
=''):
325 """Assert that 'observed' is equal to None.
327 :param observed: The observed value.
328 :param message: An optional message describing the error.
331 self
.assertThat(observed
, matcher
, message
)
333 def assertIsNotNone(self
, observed
, message
=''):
334 """Assert that 'observed' is not equal to None.
336 :param observed: The observed value.
337 :param message: An optional message describing the error.
339 matcher
= Not(Is(None))
340 self
.assertThat(observed
, matcher
, message
)
342 def assertIs(self
, expected
, observed
, message
=''):
343 """Assert that 'expected' is 'observed'.
345 :param expected: The expected value.
346 :param observed: The observed value.
347 :param message: An optional message describing the error.
349 matcher
= Is(expected
)
350 self
.assertThat(observed
, matcher
, message
)
352 def assertIsNot(self
, expected
, observed
, message
=''):
353 """Assert that 'expected' is not 'observed'."""
354 matcher
= Not(Is(expected
))
355 self
.assertThat(observed
, matcher
, message
)
357 def assertNotIn(self
, needle
, haystack
):
358 """Assert that needle is not in haystack."""
359 matcher
= Not(Contains(needle
))
360 self
.assertThat(haystack
, matcher
)
362 def assertIsInstance(self
, obj
, klass
, msg
=None):
363 if isinstance(klass
, tuple):
364 matcher
= IsInstance(*klass
)
366 matcher
= IsInstance(klass
)
367 self
.assertThat(obj
, matcher
, msg
)
369 def assertRaises(self
, excClass
, callableObj
, *args
, **kwargs
):
370 """Fail unless an exception of class excClass is thrown
371 by callableObj when invoked with arguments args and keyword
372 arguments kwargs. If a different type of exception is
373 thrown, it will not be caught, and the test case will be
374 deemed to have suffered an error, exactly as for an
375 unexpected exception.
377 class ReRaiseOtherTypes(object):
378 def match(self
, matchee
):
379 if not issubclass(matchee
[0], excClass
):
381 class CaptureMatchee(object):
382 def match(self
, matchee
):
383 self
.matchee
= matchee
[1]
384 capture
= CaptureMatchee()
385 matcher
= Raises(MatchesAll(ReRaiseOtherTypes(),
386 MatchesException(excClass
), capture
))
388 self
.assertThat(lambda: callableObj(*args
, **kwargs
), matcher
)
389 return capture
.matchee
390 failUnlessRaises
= assertRaises
392 def assertThat(self
, matchee
, matcher
, message
='', verbose
=False):
393 """Assert that matchee is matched by matcher.
395 :param matchee: An object to match with matcher.
396 :param matcher: An object meeting the testtools.Matcher protocol.
397 :raises MismatchError: When matcher does not match thing.
399 matcher
= Annotate
.if_message(message
, matcher
)
400 mismatch
= matcher
.match(matchee
)
403 existing_details
= self
.getDetails()
404 for (name
, content
) in mismatch
.get_details().items():
407 while full_name
in existing_details
:
408 full_name
= "%s-%d" % (name
, suffix
)
410 self
.addDetail(full_name
, content
)
411 raise MismatchError(matchee
, matcher
, mismatch
, verbose
)
413 def defaultTestResult(self
):
416 def expectFailure(self
, reason
, predicate
, *args
, **kwargs
):
417 """Check that a test fails in a particular way.
419 If the test fails in the expected way, a KnownFailure is caused. If it
420 succeeds an UnexpectedSuccess is caused.
422 The expected use of expectFailure is as a barrier at the point in a
423 test where the test would fail. For example:
424 >>> def test_foo(self):
425 >>> self.expectFailure("1 should be 0", self.assertNotEqual, 1, 0)
426 >>> self.assertEqual(1, 0)
428 If in the future 1 were to equal 0, the expectFailure call can simply
429 be removed. This separation preserves the original intent of the test
430 while it is in the expectFailure mode.
432 # TODO: implement with matchers.
433 self
._add
_reason
(reason
)
435 predicate(*args
, **kwargs
)
436 except self
.failureException
:
437 # GZ 2010-08-12: Don't know how to avoid exc_info cycle as the new
438 # unittest _ExpectedFailure wants old traceback
439 exc_info
= sys
.exc_info()
441 self
._report
_traceback
(exc_info
)
442 raise _ExpectedFailure(exc_info
)
446 raise _UnexpectedSuccess(reason
)
448 def getUniqueInteger(self
):
449 """Get an integer unique to this test.
451 Returns an integer that is guaranteed to be unique to this instance.
452 Use this when you need an arbitrary integer in your test, or as a
453 helper for custom anonymous factory methods.
455 return advance_iterator(self
._unique
_id
_gen
)
457 def getUniqueString(self
, prefix
=None):
458 """Get a string unique to this test.
460 Returns a string that is guaranteed to be unique to this instance. Use
461 this when you need an arbitrary string in your test, or as a helper
462 for custom anonymous factory methods.
464 :param prefix: The prefix of the string. If not provided, defaults
465 to the id of the tests.
466 :return: A bytestring of '<prefix>-<unique_int>'.
470 return '%s-%d' % (prefix
, self
.getUniqueInteger())
472 def onException(self
, exc_info
, tb_label
='traceback'):
473 """Called when an exception propogates from test code.
475 :seealso addOnException:
477 if exc_info
[0] not in [
478 TestSkipped
, _UnexpectedSuccess
, _ExpectedFailure
]:
479 self
._report
_traceback
(exc_info
, tb_label
=tb_label
)
480 for handler
in self
.__exception
_handlers
:
484 def _report_error(self
, result
, err
):
485 result
.addError(self
, details
=self
.getDetails())
488 def _report_expected_failure(self
, result
, err
):
489 result
.addExpectedFailure(self
, details
=self
.getDetails())
492 def _report_failure(self
, result
, err
):
493 result
.addFailure(self
, details
=self
.getDetails())
496 def _report_skip(self
, result
, err
):
500 reason
= "no reason given."
501 self
._add
_reason
(reason
)
502 result
.addSkip(self
, details
=self
.getDetails())
504 def _report_traceback(self
, exc_info
, tb_label
='traceback'):
505 id_gen
= self
._traceback
_id
_gens
.setdefault(
506 tb_label
, itertools
.count(0))
507 tb_id
= advance_iterator(id_gen
)
509 tb_label
= '%s-%d' % (tb_label
, tb_id
)
510 self
.addDetail(tb_label
, content
.TracebackContent(exc_info
, self
))
513 def _report_unexpected_success(self
, result
, err
):
514 result
.addUnexpectedSuccess(self
, details
=self
.getDetails())
516 def run(self
, result
=None):
517 return self
.__RunTest
(self
, self
.exception_handlers
).run(result
)
519 def _run_setup(self
, result
):
520 """Run the setUp function for this test.
522 :param result: A testtools.TestResult to report activity to.
523 :raises ValueError: If the base class setUp is not called, a
524 ValueError is raised.
527 if not self
.__setup
_called
:
529 "TestCase.setUp was not called. Have you upcalled all the "
530 "way up the hierarchy from your setUp? e.g. Call "
531 "super(%s, self).setUp() from your setUp()."
532 % self
.__class
__.__name
__)
535 def _run_teardown(self
, result
):
536 """Run the tearDown function for this test.
538 :param result: A testtools.TestResult to report activity to.
539 :raises ValueError: If the base class tearDown is not called, a
540 ValueError is raised.
542 ret
= self
.tearDown()
543 if not self
.__teardown
_called
:
545 "TestCase.tearDown was not called. Have you upcalled all the "
546 "way up the hierarchy from your tearDown? e.g. Call "
547 "super(%s, self).tearDown() from your tearDown()."
548 % self
.__class
__.__name
__)
551 def _get_test_method(self
):
552 absent_attr
= object()
554 method_name
= getattr(self
, '_testMethodName', absent_attr
)
555 if method_name
is absent_attr
:
557 method_name
= getattr(self
, '_TestCase__testMethodName')
558 return getattr(self
, method_name
)
560 def _run_test_method(self
, result
):
561 """Run the test method for this test.
563 :param result: A testtools.TestResult to report activity to.
566 return self
._get
_test
_method
()()
568 def useFixture(self
, fixture
):
569 """Use fixture in a test case.
571 The fixture will be setUp, and self.addCleanup(fixture.cleanUp) called.
573 :param fixture: The fixture to use.
574 :return: The fixture, after setting it up and scheduling a cleanup for
580 gather_details(fixture
.getDetails(), self
.getDetails())
583 self
.addCleanup(fixture
.cleanUp
)
585 gather_details
, fixture
.getDetails(), self
.getDetails())
589 super(TestCase
, self
).setUp()
590 self
.__setup
_called
= True
593 super(TestCase
, self
).tearDown()
594 unittest
.TestCase
.tearDown(self
)
595 self
.__teardown
_called
= True
598 class PlaceHolder(object):
599 """A placeholder test.
601 `PlaceHolder` implements much of the same interface as TestCase and is
602 particularly suitable for being added to TestResults.
605 def __init__(self
, test_id
, short_description
=None):
606 """Construct a `PlaceHolder`.
608 :param test_id: The id of the placeholder test.
609 :param short_description: The short description of the place holder
610 test. If not provided, the id will be used instead.
612 self
._test
_id
= test_id
613 self
._short
_description
= short_description
615 def __call__(self
, result
=None):
616 return self
.run(result
=result
)
619 internal
= [self
._test
_id
]
620 if self
._short
_description
is not None:
621 internal
.append(self
._short
_description
)
622 return "<%s.%s(%s)>" % (
623 self
.__class
__.__module
__,
624 self
.__class
__.__name
__,
625 ", ".join(map(repr, internal
)))
630 def countTestCases(self
):
639 def run(self
, result
=None):
641 result
= TestResult()
642 result
.startTest(self
)
643 result
.addSuccess(self
)
644 result
.stopTest(self
)
646 def shortDescription(self
):
647 if self
._short
_description
is None:
650 return self
._short
_description
653 class ErrorHolder(PlaceHolder
):
654 """A placeholder test that will error out when run."""
656 failureException
= None
658 def __init__(self
, test_id
, error
, short_description
=None):
659 """Construct an `ErrorHolder`.
661 :param test_id: The id of the test.
662 :param error: The exc info tuple that will be used as the test's error.
663 :param short_description: An optional short description of the test.
665 super(ErrorHolder
, self
).__init
__(
666 test_id
, short_description
=short_description
)
670 internal
= [self
._test
_id
, self
._error
]
671 if self
._short
_description
is not None:
672 internal
.append(self
._short
_description
)
673 return "<%s.%s(%s)>" % (
674 self
.__class
__.__module
__,
675 self
.__class
__.__name
__,
676 ", ".join(map(repr, internal
)))
678 def run(self
, result
=None):
680 result
= TestResult()
681 result
.startTest(self
)
682 result
.addError(self
, self
._error
)
683 result
.stopTest(self
)
686 # Python 2.4 did not know how to copy functions.
687 if types
.FunctionType
not in copy
._copy
_dispatch
:
688 copy
._copy
_dispatch
[types
.FunctionType
] = copy
._copy
_immutable
691 def clone_test_with_new_id(test
, new_id
):
692 """Copy a `TestCase`, and give the copied test a new id.
694 This is only expected to be used on tests that have been constructed but
697 newTest
= copy
.copy(test
)
698 newTest
.id = lambda: new_id
703 """A decorator to skip unit tests.
705 This is just syntactic sugar so users don't have to change any of their
706 unit tests in order to migrate to python 2.7, which provides the
707 @unittest.skip decorator.
709 def decorator(test_item
):
710 if wraps
is not None:
712 def skip_wrapper(*args
, **kwargs
):
713 raise TestCase
.skipException(reason
)
715 def skip_wrapper(test_item
):
716 test_item
.skip(reason
)
721 def skipIf(condition
, reason
):
722 """Skip a test if the condition is true."""
730 def skipUnless(condition
, reason
):
731 """Skip a test unless the condition is true."""
739 class ExpectedException
:
740 """A context manager to handle expected exceptions.
742 In Python 2.5 or later::
745 with ExpectedException(ValueError, 'fo.*'):
746 raise ValueError('foo')
748 will pass. If the raised exception has a type other than the specified
749 type, it will be re-raised. If it has a 'str()' that does not match the
750 given regular expression, an AssertionError will be raised. If no
751 exception is raised, an AssertionError will be raised.
754 def __init__(self
, exc_type
, value_re
=None):
755 """Construct an `ExpectedException`.
757 :param exc_type: The type of exception to expect.
758 :param value_re: A regular expression to match against the
759 'str()' of the raised exception.
761 self
.exc_type
= exc_type
762 self
.value_re
= value_re
767 def __exit__(self
, exc_type
, exc_value
, traceback
):
769 raise AssertionError('%s not raised.' % self
.exc_type
.__name
__)
770 if exc_type
!= self
.exc_type
:
773 matcher
= MatchesException(self
.exc_type
, self
.value_re
)
774 mismatch
= matcher
.match((exc_type
, exc_value
, traceback
))
776 raise AssertionError(mismatch
.describe())
780 # Signal that this is part of the testing framework, and that code from this
781 # should not normally appear in tracebacks.