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 (
46 ExtendedToOriginalDecorator
,
50 wraps
= try_import('functools.wraps')
52 class TestSkipped(Exception):
53 """Raised within TestCase.run() when a test is skipped."""
54 testSkipped
= try_import('unittest2.case.SkipTest', TestSkipped
)
55 TestSkipped
= try_import('unittest.case.SkipTest', TestSkipped
)
58 class _UnexpectedSuccess(Exception):
59 """An unexpected success was raised.
61 Note that this exception is private plumbing in testtools' testcase
64 _UnexpectedSuccess
= try_import(
65 'unittest2.case._UnexpectedSuccess', _UnexpectedSuccess
)
66 _UnexpectedSuccess
= try_import(
67 'unittest.case._UnexpectedSuccess', _UnexpectedSuccess
)
69 class _ExpectedFailure(Exception):
70 """An expected failure occured.
72 Note that this exception is private plumbing in testtools' testcase
75 _ExpectedFailure
= try_import(
76 'unittest2.case._ExpectedFailure', _ExpectedFailure
)
77 _ExpectedFailure
= try_import(
78 'unittest.case._ExpectedFailure', _ExpectedFailure
)
81 def run_test_with(test_runner
, **kwargs
):
82 """Decorate a test as using a specific ``RunTest``.
86 @run_test_with(CustomRunner, timeout=42)
90 The returned decorator works by setting an attribute on the decorated
91 function. `TestCase.__init__` looks for this attribute when deciding on a
92 ``RunTest`` factory. If you wish to use multiple decorators on a test
93 method, then you must either make this one the top-most decorator, or you
94 must write your decorators so that they update the wrapping function with
95 the attributes of the wrapped function. The latter is recommended style
96 anyway. ``functools.wraps``, ``functools.wrapper`` and
97 ``twisted.python.util.mergeFunctionMetadata`` can help you do this.
99 :param test_runner: A ``RunTest`` factory that takes a test case and an
100 optional list of exception handlers. See ``RunTest``.
101 :param kwargs: Keyword arguments to pass on as extra arguments to
103 :return: A decorator to be used for marking a test as needing a special
106 def decorator(function
):
107 # Set an attribute on 'function' which will inform TestCase how to
109 function
._run
_test
_with
= (
110 lambda case
, handlers
=None:
111 test_runner(case
, handlers
=handlers
, **kwargs
))
116 def _copy_content(content_object
):
117 """Make a copy of the given content object.
119 The content within ``content_object`` is iterated and saved. This is
120 useful when the source of the content is volatile, a log file in a
121 temporary directory for example.
123 :param content_object: A `content.Content` instance.
124 :return: A `content.Content` instance with the same mime-type as
125 ``content_object`` and a non-volatile copy of its content.
127 content_bytes
= list(content_object
.iter_bytes())
128 content_callback
= lambda: content_bytes
129 return content
.Content(content_object
.content_type
, content_callback
)
132 def gather_details(source_dict
, target_dict
):
133 """Merge the details from ``source_dict`` into ``target_dict``.
135 :param source_dict: A dictionary of details will be gathered.
136 :param target_dict: A dictionary into which details will be gathered.
138 for name
, content_object
in source_dict
.items():
140 disambiguator
= itertools
.count(1)
141 while new_name
in target_dict
:
142 new_name
= '%s-%d' % (name
, advance_iterator(disambiguator
))
144 target_dict
[name
] = _copy_content(content_object
)
147 class TestCase(unittest
.TestCase
):
148 """Extensions to the basic TestCase.
150 :ivar exception_handlers: Exceptions to catch from setUp, runTest and
151 tearDown. This list is able to be modified at any time and consists of
152 (exception_class, handler(case, result, exception_value)) pairs.
153 :cvar run_tests_with: A factory to make the ``RunTest`` to run tests with.
154 Defaults to ``RunTest``. The factory is expected to take a test case
155 and an optional list of exception handlers.
158 skipException
= TestSkipped
160 run_tests_with
= RunTest
162 def __init__(self
, *args
, **kwargs
):
163 """Construct a TestCase.
165 :param testMethod: The name of the method to run.
166 :keyword runTest: Optional class to use to execute the test. If not
167 supplied ``RunTest`` is used. The instance to be used is created
168 when run() is invoked, so will be fresh each time. Overrides
169 ``TestCase.run_tests_with`` if given.
171 runTest
= kwargs
.pop('runTest', None)
172 super(TestCase
, self
).__init
__(*args
, **kwargs
)
174 self
._unique
_id
_gen
= itertools
.count(1)
175 # Generators to ensure unique traceback ids. Maps traceback label to
177 self
._traceback
_id
_gens
= {}
178 self
.__setup
_called
= False
179 self
.__teardown
_called
= False
180 # __details is lazy-initialized so that a constructed-but-not-run
181 # TestCase is safe to use with clone_test_with_new_id.
182 self
.__details
= None
183 test_method
= self
._get
_test
_method
()
186 test_method
, '_run_test_with', self
.run_tests_with
)
187 self
.__RunTest
= runTest
188 self
.__exception
_handlers
= []
189 self
.exception_handlers
= [
190 (self
.skipException
, self
._report
_skip
),
191 (self
.failureException
, self
._report
_failure
),
192 (_ExpectedFailure
, self
._report
_expected
_failure
),
193 (_UnexpectedSuccess
, self
._report
_unexpected
_success
),
194 (Exception, self
._report
_error
),
196 if sys
.version_info
< (2, 6):
197 # Catch old-style string exceptions with None as the instance
198 self
.exception_handlers
.append((type(None), self
._report
_error
))
200 def __eq__(self
, other
):
201 eq
= getattr(unittest
.TestCase
, '__eq__', None)
202 if eq
is not None and not unittest
.TestCase
.__eq
__(self
, other
):
204 return self
.__dict
__ == other
.__dict
__
207 # We add id to the repr because it makes testing testtools easier.
208 return "<%s id=0x%0x>" % (self
.id(), id(self
))
210 def addDetail(self
, name
, content_object
):
211 """Add a detail to be reported with this test's outcome.
213 For more details see pydoc testtools.TestResult.
215 :param name: The name to give this detail.
216 :param content_object: The content object for this detail. See
217 testtools.content for more detail.
219 if self
.__details
is None:
221 self
.__details
[name
] = content_object
223 def getDetails(self
):
224 """Get the details dict that will be reported with this test's outcome.
226 For more details see pydoc testtools.TestResult.
228 if self
.__details
is None:
230 return self
.__details
232 def patch(self
, obj
, attribute
, value
):
233 """Monkey-patch 'obj.attribute' to 'value' while the test is running.
235 If 'obj' has no attribute, then the monkey-patch will still go ahead,
236 and the attribute will be deleted instead of restored to its original
239 :param obj: The object to patch. Can be anything.
240 :param attribute: The attribute on 'obj' to patch.
241 :param value: The value to set 'obj.attribute' to.
243 self
.addCleanup(patch(obj
, attribute
, value
))
245 def shortDescription(self
):
248 def skipTest(self
, reason
):
249 """Cause this test to be skipped.
251 This raises self.skipException(reason). skipException is raised
252 to permit a skip to be triggered at any point (during setUp or the
253 testMethod itself). The run() method catches skipException and
254 translates that into a call to the result objects addSkip method.
256 :param reason: The reason why the test is being skipped. This must
257 support being cast into a unicode string for reporting.
259 raise self
.skipException(reason
)
261 # skipTest is how python2.7 spells this. Sometime in the future
262 # This should be given a deprecation decorator - RBC 20100611.
265 def _formatTypes(self
, classOrIterable
):
266 """Format a class or a bunch of classes for display in an error."""
267 className
= getattr(classOrIterable
, '__name__', None)
268 if className
is None:
269 className
= ', '.join(klass
.__name
__ for klass
in classOrIterable
)
272 def addCleanup(self
, function
, *arguments
, **keywordArguments
):
273 """Add a cleanup function to be called after tearDown.
275 Functions added with addCleanup will be called in reverse order of
276 adding after tearDown, or after setUp if setUp raises an exception.
278 If a function added with addCleanup raises an exception, the error
279 will be recorded as a test error, and the next cleanup will then be
282 Cleanup functions are always called before a test finishes running,
283 even if setUp is aborted by an exception.
285 self
._cleanups
.append((function
, arguments
, keywordArguments
))
287 def addOnException(self
, handler
):
288 """Add a handler to be called when an exception occurs in test code.
290 This handler cannot affect what result methods are called, and is
291 called before any outcome is called on the result object. An example
292 use for it is to add some diagnostic state to the test details dict
293 which is expensive to calculate and not interesting for reporting in
296 Handlers are called before the outcome (such as addFailure) that
297 the exception has caused.
299 Handlers are called in first-added, first-called order, and if they
300 raise an exception, that will propogate out of the test running
301 machinery, halting test processing. As a result, do not call code that
302 may unreasonably fail.
304 self
.__exception
_handlers
.append(handler
)
306 def _add_reason(self
, reason
):
307 self
.addDetail('reason', content
.text_content(reason
))
309 def assertEqual(self
, expected
, observed
, message
=''):
310 """Assert that 'expected' is equal to 'observed'.
312 :param expected: The expected value.
313 :param observed: The observed value.
314 :param message: An optional message to include in the error.
316 matcher
= Equals(expected
)
317 self
.assertThat(observed
, matcher
, message
)
319 failUnlessEqual
= assertEquals
= assertEqual
321 def assertIn(self
, needle
, haystack
):
322 """Assert that needle is in haystack."""
323 self
.assertThat(haystack
, Contains(needle
))
325 def assertIsNone(self
, observed
, message
=''):
326 """Assert that 'observed' is equal to None.
328 :param observed: The observed value.
329 :param message: An optional message describing the error.
332 self
.assertThat(observed
, matcher
, message
)
334 def assertIsNotNone(self
, observed
, message
=''):
335 """Assert that 'observed' is not equal to None.
337 :param observed: The observed value.
338 :param message: An optional message describing the error.
340 matcher
= Not(Is(None))
341 self
.assertThat(observed
, matcher
, message
)
343 def assertIs(self
, expected
, observed
, message
=''):
344 """Assert that 'expected' is 'observed'.
346 :param expected: The expected value.
347 :param observed: The observed value.
348 :param message: An optional message describing the error.
350 matcher
= Is(expected
)
351 self
.assertThat(observed
, matcher
, message
)
353 def assertIsNot(self
, expected
, observed
, message
=''):
354 """Assert that 'expected' is not 'observed'."""
355 matcher
= Not(Is(expected
))
356 self
.assertThat(observed
, matcher
, message
)
358 def assertNotIn(self
, needle
, haystack
):
359 """Assert that needle is not in haystack."""
360 matcher
= Not(Contains(needle
))
361 self
.assertThat(haystack
, matcher
)
363 def assertIsInstance(self
, obj
, klass
, msg
=None):
364 if isinstance(klass
, tuple):
365 matcher
= IsInstance(*klass
)
367 matcher
= IsInstance(klass
)
368 self
.assertThat(obj
, matcher
, msg
)
370 def assertRaises(self
, excClass
, callableObj
, *args
, **kwargs
):
371 """Fail unless an exception of class excClass is thrown
372 by callableObj when invoked with arguments args and keyword
373 arguments kwargs. If a different type of exception is
374 thrown, it will not be caught, and the test case will be
375 deemed to have suffered an error, exactly as for an
376 unexpected exception.
378 class ReRaiseOtherTypes(object):
379 def match(self
, matchee
):
380 if not issubclass(matchee
[0], excClass
):
382 class CaptureMatchee(object):
383 def match(self
, matchee
):
384 self
.matchee
= matchee
[1]
385 capture
= CaptureMatchee()
386 matcher
= Raises(MatchesAll(ReRaiseOtherTypes(),
387 MatchesException(excClass
), capture
))
388 our_callable
= Nullary(callableObj
, *args
, **kwargs
)
389 self
.assertThat(our_callable
, matcher
)
390 return capture
.matchee
391 failUnlessRaises
= assertRaises
393 def assertThat(self
, matchee
, matcher
, message
='', verbose
=False):
394 """Assert that matchee is matched by matcher.
396 :param matchee: An object to match with matcher.
397 :param matcher: An object meeting the testtools.Matcher protocol.
398 :raises MismatchError: When matcher does not match thing.
400 matcher
= Annotate
.if_message(message
, matcher
)
401 mismatch
= matcher
.match(matchee
)
404 existing_details
= self
.getDetails()
405 for (name
, content
) in mismatch
.get_details().items():
408 while full_name
in existing_details
:
409 full_name
= "%s-%d" % (name
, suffix
)
411 self
.addDetail(full_name
, content
)
412 raise MismatchError(matchee
, matcher
, mismatch
, verbose
)
414 def defaultTestResult(self
):
417 def expectFailure(self
, reason
, predicate
, *args
, **kwargs
):
418 """Check that a test fails in a particular way.
420 If the test fails in the expected way, a KnownFailure is caused. If it
421 succeeds an UnexpectedSuccess is caused.
423 The expected use of expectFailure is as a barrier at the point in a
424 test where the test would fail. For example:
425 >>> def test_foo(self):
426 >>> self.expectFailure("1 should be 0", self.assertNotEqual, 1, 0)
427 >>> self.assertEqual(1, 0)
429 If in the future 1 were to equal 0, the expectFailure call can simply
430 be removed. This separation preserves the original intent of the test
431 while it is in the expectFailure mode.
433 # TODO: implement with matchers.
434 self
._add
_reason
(reason
)
436 predicate(*args
, **kwargs
)
437 except self
.failureException
:
438 # GZ 2010-08-12: Don't know how to avoid exc_info cycle as the new
439 # unittest _ExpectedFailure wants old traceback
440 exc_info
= sys
.exc_info()
442 self
._report
_traceback
(exc_info
)
443 raise _ExpectedFailure(exc_info
)
447 raise _UnexpectedSuccess(reason
)
449 def getUniqueInteger(self
):
450 """Get an integer unique to this test.
452 Returns an integer that is guaranteed to be unique to this instance.
453 Use this when you need an arbitrary integer in your test, or as a
454 helper for custom anonymous factory methods.
456 return advance_iterator(self
._unique
_id
_gen
)
458 def getUniqueString(self
, prefix
=None):
459 """Get a string unique to this test.
461 Returns a string that is guaranteed to be unique to this instance. Use
462 this when you need an arbitrary string in your test, or as a helper
463 for custom anonymous factory methods.
465 :param prefix: The prefix of the string. If not provided, defaults
466 to the id of the tests.
467 :return: A bytestring of '<prefix>-<unique_int>'.
471 return '%s-%d' % (prefix
, self
.getUniqueInteger())
473 def onException(self
, exc_info
, tb_label
='traceback'):
474 """Called when an exception propogates from test code.
476 :seealso addOnException:
478 if exc_info
[0] not in [
479 TestSkipped
, _UnexpectedSuccess
, _ExpectedFailure
]:
480 self
._report
_traceback
(exc_info
, tb_label
=tb_label
)
481 for handler
in self
.__exception
_handlers
:
485 def _report_error(self
, result
, err
):
486 result
.addError(self
, details
=self
.getDetails())
489 def _report_expected_failure(self
, result
, err
):
490 result
.addExpectedFailure(self
, details
=self
.getDetails())
493 def _report_failure(self
, result
, err
):
494 result
.addFailure(self
, details
=self
.getDetails())
497 def _report_skip(self
, result
, err
):
501 reason
= "no reason given."
502 self
._add
_reason
(reason
)
503 result
.addSkip(self
, details
=self
.getDetails())
505 def _report_traceback(self
, exc_info
, tb_label
='traceback'):
506 id_gen
= self
._traceback
_id
_gens
.setdefault(
507 tb_label
, itertools
.count(0))
508 tb_id
= advance_iterator(id_gen
)
510 tb_label
= '%s-%d' % (tb_label
, tb_id
)
511 self
.addDetail(tb_label
, content
.TracebackContent(exc_info
, self
))
514 def _report_unexpected_success(self
, result
, err
):
515 result
.addUnexpectedSuccess(self
, details
=self
.getDetails())
517 def run(self
, result
=None):
518 return self
.__RunTest
(self
, self
.exception_handlers
).run(result
)
520 def _run_setup(self
, result
):
521 """Run the setUp function for this test.
523 :param result: A testtools.TestResult to report activity to.
524 :raises ValueError: If the base class setUp is not called, a
525 ValueError is raised.
528 if not self
.__setup
_called
:
530 "TestCase.setUp was not called. Have you upcalled all the "
531 "way up the hierarchy from your setUp? e.g. Call "
532 "super(%s, self).setUp() from your setUp()."
533 % self
.__class
__.__name
__)
536 def _run_teardown(self
, result
):
537 """Run the tearDown function for this test.
539 :param result: A testtools.TestResult to report activity to.
540 :raises ValueError: If the base class tearDown is not called, a
541 ValueError is raised.
543 ret
= self
.tearDown()
544 if not self
.__teardown
_called
:
546 "TestCase.tearDown was not called. Have you upcalled all the "
547 "way up the hierarchy from your tearDown? e.g. Call "
548 "super(%s, self).tearDown() from your tearDown()."
549 % self
.__class
__.__name
__)
552 def _get_test_method(self
):
553 absent_attr
= object()
555 method_name
= getattr(self
, '_testMethodName', absent_attr
)
556 if method_name
is absent_attr
:
558 method_name
= getattr(self
, '_TestCase__testMethodName')
559 return getattr(self
, method_name
)
561 def _run_test_method(self
, result
):
562 """Run the test method for this test.
564 :param result: A testtools.TestResult to report activity to.
567 return self
._get
_test
_method
()()
569 def useFixture(self
, fixture
):
570 """Use fixture in a test case.
572 The fixture will be setUp, and self.addCleanup(fixture.cleanUp) called.
574 :param fixture: The fixture to use.
575 :return: The fixture, after setting it up and scheduling a cleanup for
581 gather_details(fixture
.getDetails(), self
.getDetails())
584 self
.addCleanup(fixture
.cleanUp
)
586 gather_details
, fixture
.getDetails(), self
.getDetails())
590 super(TestCase
, self
).setUp()
591 self
.__setup
_called
= True
594 super(TestCase
, self
).tearDown()
595 unittest
.TestCase
.tearDown(self
)
596 self
.__teardown
_called
= True
599 class PlaceHolder(object):
600 """A placeholder test.
602 `PlaceHolder` implements much of the same interface as TestCase and is
603 particularly suitable for being added to TestResults.
606 failureException
= None
608 def __init__(self
, test_id
, short_description
=None, details
=None,
609 outcome
='addSuccess', error
=None):
610 """Construct a `PlaceHolder`.
612 :param test_id: The id of the placeholder test.
613 :param short_description: The short description of the place holder
614 test. If not provided, the id will be used instead.
615 :param details: Outcome details as accepted by addSuccess etc.
616 :param outcome: The outcome to call. Defaults to 'addSuccess'.
618 self
._test
_id
= test_id
619 self
._short
_description
= short_description
620 self
._details
= details
or {}
621 self
._outcome
= outcome
622 if error
is not None:
623 self
._details
['traceback'] = content
.TracebackContent(error
, self
)
625 def __call__(self
, result
=None):
626 return self
.run(result
=result
)
629 internal
= [self
._outcome
, self
._test
_id
, self
._details
]
630 if self
._short
_description
is not None:
631 internal
.append(self
._short
_description
)
632 return "<%s.%s(%s)>" % (
633 self
.__class
__.__module
__,
634 self
.__class
__.__name
__,
635 ", ".join(map(repr, internal
)))
640 def countTestCases(self
):
649 def _result(self
, result
):
653 return ExtendedToOriginalDecorator(result
)
655 def run(self
, result
=None):
656 result
= self
._result
(result
)
657 result
.startTest(self
)
658 outcome
= getattr(result
, self
._outcome
)
659 outcome(self
, details
=self
._details
)
660 result
.stopTest(self
)
662 def shortDescription(self
):
663 if self
._short
_description
is None:
666 return self
._short
_description
669 def ErrorHolder(test_id
, error
, short_description
=None, details
=None):
670 """Construct an `ErrorHolder`.
672 :param test_id: The id of the test.
673 :param error: The exc info tuple that will be used as the test's error.
674 This is inserted into the details as 'traceback' - any existing key
676 :param short_description: An optional short description of the test.
677 :param details: Outcome details as accepted by addSuccess etc.
679 return PlaceHolder(test_id
, short_description
=short_description
,
680 details
=details
, outcome
='addError', error
=error
)
683 # Python 2.4 did not know how to copy functions.
684 if types
.FunctionType
not in copy
._copy
_dispatch
:
685 copy
._copy
_dispatch
[types
.FunctionType
] = copy
._copy
_immutable
688 def clone_test_with_new_id(test
, new_id
):
689 """Copy a `TestCase`, and give the copied test a new id.
691 This is only expected to be used on tests that have been constructed but
694 newTest
= copy
.copy(test
)
695 newTest
.id = lambda: new_id
700 """A decorator to skip unit tests.
702 This is just syntactic sugar so users don't have to change any of their
703 unit tests in order to migrate to python 2.7, which provides the
704 @unittest.skip decorator.
706 def decorator(test_item
):
707 if wraps
is not None:
709 def skip_wrapper(*args
, **kwargs
):
710 raise TestCase
.skipException(reason
)
712 def skip_wrapper(test_item
):
713 test_item
.skip(reason
)
718 def skipIf(condition
, reason
):
719 """Skip a test if the condition is true."""
727 def skipUnless(condition
, reason
):
728 """Skip a test unless the condition is true."""
736 class ExpectedException
:
737 """A context manager to handle expected exceptions.
739 In Python 2.5 or later::
742 with ExpectedException(ValueError, 'fo.*'):
743 raise ValueError('foo')
745 will pass. If the raised exception has a type other than the specified
746 type, it will be re-raised. If it has a 'str()' that does not match the
747 given regular expression, an AssertionError will be raised. If no
748 exception is raised, an AssertionError will be raised.
751 def __init__(self
, exc_type
, value_re
=None):
752 """Construct an `ExpectedException`.
754 :param exc_type: The type of exception to expect.
755 :param value_re: A regular expression to match against the
756 'str()' of the raised exception.
758 self
.exc_type
= exc_type
759 self
.value_re
= value_re
764 def __exit__(self
, exc_type
, exc_value
, traceback
):
766 raise AssertionError('%s not raised.' % self
.exc_type
.__name
__)
767 if exc_type
!= self
.exc_type
:
770 matcher
= MatchesException(self
.exc_type
, self
.value_re
)
771 mismatch
= matcher
.match((exc_type
, exc_value
, traceback
))
773 raise AssertionError(mismatch
.describe())
777 class Nullary(object):
778 """Turn a callable into a nullary callable.
780 The advantage of this over ``lambda: f(*args, **kwargs)`` is that it
781 preserves the ``repr()`` of ``f``.
784 def __init__(self
, callable_object
, *args
, **kwargs
):
785 self
._callable
_object
= callable_object
787 self
._kwargs
= kwargs
790 return self
._callable
_object
(*self
._args
, **self
._kwargs
)
793 return repr(self
._callable
_object
)
796 # Signal that this is part of the testing framework, and that code from this
797 # should not normally appear in tracebacks.