s3: Handle EINTR from sys_poll correctly
[Samba.git] / lib / testtools / MANUAL
blob7e7853c7e7b148a5501bee1568c226c81a1133b5
1 ======
2 Manual
3 ======
5 Introduction
6 ------------
8 This document provides overview of the features provided by testtools.  Refer
9 to the API docs (i.e. docstrings) for full details on a particular feature.
11 Extensions to TestCase
12 ----------------------
14 Custom exception handling
15 ~~~~~~~~~~~~~~~~~~~~~~~~~
17 testtools provides a way to control how test exceptions are handled.  To do
18 this, add a new exception to self.exception_handlers on a TestCase.  For
19 example::
21     >>> self.exception_handlers.insert(-1, (ExceptionClass, handler)).
23 Having done this, if any of setUp, tearDown, or the test method raise
24 ExceptionClass, handler will be called with the test case, test result and the
25 raised exception.
27 Controlling test execution
28 ~~~~~~~~~~~~~~~~~~~~~~~~~~
30 If you want to control more than just how exceptions are raised, you can
31 provide a custom `RunTest` to a TestCase.  The `RunTest` object can change
32 everything about how the test executes.
34 To work with `testtools.TestCase`, a `RunTest` must have a factory that takes
35 a test and an optional list of exception handlers.  Instances returned by the
36 factory must have a `run()` method that takes an optional `TestResult` object.
38 The default is `testtools.runtest.RunTest` and calls 'setUp', the test method
39 and 'tearDown' in the normal, vanilla way that Python's standard unittest
40 does.
42 To specify a `RunTest` for all the tests in a `TestCase` class, do something
43 like this::
45   class SomeTests(TestCase):
46       run_tests_with = CustomRunTestFactory
48 To specify a `RunTest` for a specific test in a `TestCase` class, do::
50   class SomeTests(TestCase):
51       @run_test_with(CustomRunTestFactory, extra_arg=42, foo='whatever')
52       def test_something(self):
53           pass
55 In addition, either of these can be overridden by passing a factory in to the
56 `TestCase` constructor with the optional 'runTest' argument.
58 TestCase.addCleanup
59 ~~~~~~~~~~~~~~~~~~~
61 addCleanup is a robust way to arrange for a cleanup function to be called
62 before tearDown.  This is a powerful and simple alternative to putting cleanup
63 logic in a try/finally block or tearDown method.  e.g.::
65     def test_foo(self):
66         foo.lock()
67         self.addCleanup(foo.unlock)
68         ...
70 Cleanups can also report multiple errors, if appropriate by wrapping them in
71 a testtools.MultipleExceptions object::
73     raise MultipleExceptions(exc_info1, exc_info2)
76 TestCase.addOnException
77 ~~~~~~~~~~~~~~~~~~~~~~~
79 addOnException adds an exception handler that will be called from the test
80 framework when it detects an exception from your test code. The handler is
81 given the exc_info for the exception, and can use this opportunity to attach
82 more data (via the addDetails API) and potentially other uses.
85 TestCase.patch
86 ~~~~~~~~~~~~~~
88 ``patch`` is a convenient way to monkey-patch a Python object for the duration
89 of your test.  It's especially useful for testing legacy code.  e.g.::
91     def test_foo(self):
92         my_stream = StringIO()
93         self.patch(sys, 'stderr', my_stream)
94         run_some_code_that_prints_to_stderr()
95         self.assertEqual('', my_stream.getvalue())
97 The call to ``patch`` above masks sys.stderr with 'my_stream' so that anything
98 printed to stderr will be captured in a StringIO variable that can be actually
99 tested. Once the test is done, the real sys.stderr is restored to its rightful
100 place.
103 TestCase.skipTest
104 ~~~~~~~~~~~~~~~~~
106 ``skipTest`` is a simple way to have a test stop running and be reported as a
107 skipped test, rather than a success/error/failure. This is an alternative to
108 convoluted logic during test loading, permitting later and more localized
109 decisions about the appropriateness of running a test. Many reasons exist to
110 skip a test - for instance when a dependency is missing, or if the test is
111 expensive and should not be run while on laptop battery power, or if the test
112 is testing an incomplete feature (this is sometimes called a TODO). Using this
113 feature when running your test suite with a TestResult object that is missing
114 the ``addSkip`` method will result in the ``addError`` method being invoked
115 instead. ``skipTest`` was previously known as ``skip`` but as Python 2.7 adds
116 ``skipTest`` support, the ``skip`` name is now deprecated (but no warning
117 is emitted yet - some time in the future we may do so).
119 TestCase.useFixture
120 ~~~~~~~~~~~~~~~~~~~
122 ``useFixture(fixture)`` calls setUp on the fixture, schedules a cleanup to 
123 clean it up, and schedules a cleanup to attach all details held by the 
124 fixture to the details dict of the test case. The fixture object should meet
125 the ``fixtures.Fixture`` protocol (version 0.3.4 or newer). This is useful
126 for moving code out of setUp and tearDown methods and into composable side
127 classes.
130 New assertion methods
131 ~~~~~~~~~~~~~~~~~~~~~
133 testtools adds several assertion methods:
135  * assertIn
136  * assertNotIn
137  * assertIs
138  * assertIsNot
139  * assertIsInstance
140  * assertThat
143 Improved assertRaises
144 ~~~~~~~~~~~~~~~~~~~~~
146 TestCase.assertRaises returns the caught exception.  This is useful for
147 asserting more things about the exception than just the type::
149         error = self.assertRaises(UnauthorisedError, thing.frobnicate)
150         self.assertEqual('bob', error.username)
151         self.assertEqual('User bob cannot frobnicate', str(error))
153 Note that this is incompatible with the assertRaises in unittest2/Python2.7.
154 While we have no immediate plans to change to be compatible consider using the
155 new assertThat facility instead::
157         self.assertThat(
158             lambda: thing.frobnicate('foo', 'bar'),
159             Raises(MatchesException(UnauthorisedError('bob')))
161 There is also a convenience function to handle this common case::
163         self.assertThat(
164             lambda: thing.frobnicate('foo', 'bar'),
165             raises(UnauthorisedError('bob')))
168 TestCase.assertThat
169 ~~~~~~~~~~~~~~~~~~~
171 assertThat is a clean way to write complex assertions without tying them to
172 the TestCase inheritance hierarchy (and thus making them easier to reuse).
174 assertThat takes an object to be matched, and a matcher, and fails if the
175 matcher does not match the matchee.
177 See pydoc testtools.Matcher for the protocol that matchers need to implement.
179 testtools includes some matchers in testtools.matchers.
180 python -c 'import testtools.matchers; print testtools.matchers.__all__' will
181 list those matchers.
183 An example using the DocTestMatches matcher which uses doctests example
184 matching logic::
186     def test_foo(self):
187         self.assertThat([1,2,3,4], DocTestMatches('[1, 2, 3, 4]'))
190 Creation methods
191 ~~~~~~~~~~~~~~~~
193 testtools.TestCase implements creation methods called ``getUniqueString`` and
194 ``getUniqueInteger``.  See pages 419-423 of *xUnit Test Patterns* by Meszaros
195 for a detailed discussion of creation methods.
198 Test renaming
199 ~~~~~~~~~~~~~
201 ``testtools.clone_test_with_new_id`` is a function to copy a test case
202 instance to one with a new name.  This is helpful for implementing test
203 parameterization.
206 Extensions to TestResult
207 ------------------------
209 TestResult.addSkip
210 ~~~~~~~~~~~~~~~~~~
212 This method is called on result objects when a test skips. The
213 ``testtools.TestResult`` class records skips in its ``skip_reasons`` instance
214 dict. The can be reported on in much the same way as succesful tests.
217 TestResult.time
218 ~~~~~~~~~~~~~~~
220 This method controls the time used by a TestResult, permitting accurate
221 timing of test results gathered on different machines or in different threads.
222 See pydoc testtools.TestResult.time for more details.
225 ThreadsafeForwardingResult
226 ~~~~~~~~~~~~~~~~~~~~~~~~~~
228 A TestResult which forwards activity to another test result, but synchronises
229 on a semaphore to ensure that all the activity for a single test arrives in a
230 batch. This allows simple TestResults which do not expect concurrent test
231 reporting to be fed the activity from multiple test threads, or processes.
233 Note that when you provide multiple errors for a single test, the target sees
234 each error as a distinct complete test.
237 TextTestResult
238 ~~~~~~~~~~~~~~
240 A TestResult that provides a text UI very similar to the Python standard
241 library UI. Key differences are that its supports the extended outcomes and
242 details API, and is completely encapsulated into the result object, permitting
243 it to be used without a 'TestRunner' object. Not all the Python 2.7 outcomes
244 are displayed (yet). It is also a 'quiet' result with no dots or verbose mode.
245 These limitations will be corrected soon.
248 Test Doubles
249 ~~~~~~~~~~~~
251 In testtools.testresult.doubles there are three test doubles that testtools
252 uses for its own testing: Python26TestResult, Python27TestResult,
253 ExtendedTestResult. These TestResult objects implement a single variation of
254 the TestResult API each, and log activity to a list self._events. These are
255 made available for the convenience of people writing their own extensions.
258 startTestRun and stopTestRun
259 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
261 Python 2.7 added hooks 'startTestRun' and 'stopTestRun' which are called
262 before and after the entire test run. 'stopTestRun' is particularly useful for
263 test results that wish to produce summary output.
265 testtools.TestResult provides empty startTestRun and stopTestRun methods, and
266 the default testtools runner will call these methods appropriately.
269 Extensions to TestSuite
270 -----------------------
272 ConcurrentTestSuite
273 ~~~~~~~~~~~~~~~~~~~
275 A TestSuite for parallel testing. This is used in conjuction with a helper that
276 runs a single suite in some parallel fashion (for instance, forking, handing
277 off to a subprocess, to a compute cloud, or simple threads).
278 ConcurrentTestSuite uses the helper to get a number of separate runnable
279 objects with a run(result), runs them all in threads using the
280 ThreadsafeForwardingResult to coalesce their activity.
283 Running tests
284 -------------
286 testtools provides a convenient way to run a test suite using the testtools
287 result object: python -m testtools.run testspec [testspec...].
289 To run tests with Python 2.4, you'll have to do something like:
290   python2.4 /path/to/testtools/run.py testspec [testspec ...].
293 Test discovery
294 --------------
296 testtools includes a backported version of the Python 2.7 glue for using the
297 discover test discovery module. If you either have Python 2.7/3.1 or newer, or
298 install the 'discover' module, then you can invoke discovery::
300     python -m testtools.run discover [path]
302 For more information see the Python 2.7 unittest documentation, or::
304     python -m testtools.run --help
307 Twisted support
308 ---------------
310 Support for running Twisted tests is very experimental right now.  You
311 shouldn't really do it.  However, if you are going to, here are some tips for
312 converting your Trial tests into testtools tests.
314  * Use the AsynchronousDeferredRunTest runner
315  * Make sure to upcall to setUp and tearDown
316  * Don't use setUpClass or tearDownClass
317  * Don't expect setting .todo, .timeout or .skip attributes to do anything
318  * flushLoggedErrors is not there for you.  Sorry.
319  * assertFailure is not there for you.  Even more sorry.
322 General helpers
323 ---------------
325 Lots of the time we would like to conditionally import modules.  testtools
326 needs to do this itself, and graciously extends the ability to its users.
328 Instead of::
330     try:
331         from twisted.internet import defer
332     except ImportError:
333         defer = None
335 You can do::
337     defer = try_import('twisted.internet.defer')
340 Instead of::
342     try:
343         from StringIO import StringIO
344     except ImportError:
345         from io import StringIO
347 You can do::
349     StringIO = try_imports(['StringIO.StringIO', 'io.StringIO'])