testtools: Import new upstream snapshot.
[nivanova/samba-autobuild/.git] / lib / testtools / MANUAL
1 ======
2 Manual
3 ======
4
5 Introduction
6 ------------
7
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.
10
11 Extensions to TestCase
12 ----------------------
13
14 Custom exception handling
15 ~~~~~~~~~~~~~~~~~~~~~~~~~
16
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::
20
21     >>> self.exception_handlers.insert(-1, (ExceptionClass, handler)).
22
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.
26
27 Controlling test execution
28 ~~~~~~~~~~~~~~~~~~~~~~~~~~
29
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.
33
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.
37
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.
41
42 To specify a `RunTest` for all the tests in a `TestCase` class, do something
43 like this::
44
45   class SomeTests(TestCase):
46       run_tests_with = CustomRunTestFactory
47
48 To specify a `RunTest` for a specific test in a `TestCase` class, do::
49
50   class SomeTests(TestCase):
51       @run_test_with(CustomRunTestFactory, extra_arg=42, foo='whatever')
52       def test_something(self):
53           pass
54
55 In addition, either of these can be overridden by passing a factory in to the
56 `TestCase` constructor with the optional 'runTest' argument.
57
58 TestCase.addCleanup
59 ~~~~~~~~~~~~~~~~~~~
60
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.::
64
65     def test_foo(self):
66         foo.lock()
67         self.addCleanup(foo.unlock)
68         ...
69
70 Cleanups can also report multiple errors, if appropriate by wrapping them in
71 a testtools.MultipleExceptions object::
72
73     raise MultipleExceptions(exc_info1, exc_info2)
74
75
76 TestCase.addOnException
77 ~~~~~~~~~~~~~~~~~~~~~~~
78
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.
83
84
85 TestCase.patch
86 ~~~~~~~~~~~~~~
87
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.::
90
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())
96
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.
101
102
103 TestCase.skipTest
104 ~~~~~~~~~~~~~~~~~
105
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).
118
119 TestCase.useFixture
120 ~~~~~~~~~~~~~~~~~~~
121
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.
128
129
130 New assertion methods
131 ~~~~~~~~~~~~~~~~~~~~~
132
133 testtools adds several assertion methods:
134
135  * assertIn
136  * assertNotIn
137  * assertIs
138  * assertIsNot
139  * assertIsInstance
140  * assertThat
141
142
143 Improved assertRaises
144 ~~~~~~~~~~~~~~~~~~~~~
145
146 TestCase.assertRaises returns the caught exception.  This is useful for
147 asserting more things about the exception than just the type::
148
149         error = self.assertRaises(UnauthorisedError, thing.frobnicate)
150         self.assertEqual('bob', error.username)
151         self.assertEqual('User bob cannot frobnicate', str(error))
152
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::
156
157         self.assertThat(
158             lambda: thing.frobnicate('foo', 'bar'),
159             Raises(MatchesException(UnauthorisedError('bob')))
160
161 There is also a convenience function to handle this common case::
162
163         self.assertThat(
164             lambda: thing.frobnicate('foo', 'bar'),
165             raises(UnauthorisedError('bob')))
166
167
168 TestCase.assertThat
169 ~~~~~~~~~~~~~~~~~~~
170
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).
173
174 assertThat takes an object to be matched, and a matcher, and fails if the
175 matcher does not match the matchee.
176
177 See pydoc testtools.Matcher for the protocol that matchers need to implement.
178
179 testtools includes some matchers in testtools.matchers.
180 python -c 'import testtools.matchers; print testtools.matchers.__all__' will
181 list those matchers.
182
183 An example using the DocTestMatches matcher which uses doctests example
184 matching logic::
185
186     def test_foo(self):
187         self.assertThat([1,2,3,4], DocTestMatches('[1, 2, 3, 4]'))
188
189
190 Creation methods
191 ~~~~~~~~~~~~~~~~
192
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.
196
197
198 Test renaming
199 ~~~~~~~~~~~~~
200
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.
204
205
206 Extensions to TestResult
207 ------------------------
208
209 TestResult.addSkip
210 ~~~~~~~~~~~~~~~~~~
211
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.
215
216
217 TestResult.time
218 ~~~~~~~~~~~~~~~
219
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.
223
224
225 ThreadsafeForwardingResult
226 ~~~~~~~~~~~~~~~~~~~~~~~~~~
227
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.
232
233 Note that when you provide multiple errors for a single test, the target sees
234 each error as a distinct complete test.
235
236
237 TextTestResult
238 ~~~~~~~~~~~~~~
239
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.
246
247
248 Test Doubles
249 ~~~~~~~~~~~~
250
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.
256
257
258 startTestRun and stopTestRun
259 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
260
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.
264
265 testtools.TestResult provides empty startTestRun and stopTestRun methods, and
266 the default testtools runner will call these methods appropriately.
267
268
269 Extensions to TestSuite
270 -----------------------
271
272 ConcurrentTestSuite
273 ~~~~~~~~~~~~~~~~~~~
274
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.
281
282
283 Running tests
284 -------------
285
286 testtools provides a convenient way to run a test suite using the testtools
287 result object: python -m testtools.run testspec [testspec...].
288
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 ...].
291
292
293 Test discovery
294 --------------
295
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::
299
300     python -m testtools.run discover [path]
301
302 For more information see the Python 2.7 unittest documentation, or::
303
304     python -m testtools.run --help
305
306
307 Twisted support
308 ---------------
309
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.
313
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.
320
321
322 General helpers
323 ---------------
324
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.
327
328 Instead of::
329
330     try:
331         from twisted.internet import defer
332     except ImportError:
333         defer = None
334
335 You can do::
336
337     defer = try_import('twisted.internet.defer')
338
339
340 Instead of::
341
342     try:
343         from StringIO import StringIO
344     except ImportError:
345         from io import StringIO
346
347 You can do::
348
349     StringIO = try_imports(['StringIO.StringIO', 'io.StringIO'])