Michael Merickel
2017-06-18 75c30dfe18b26ca04efae2acbe35052fa0d93ed6
commit | author | age
6ee49a 1 .. index::
CM 2    single: unit testing
3    single: integration testing
4    single: functional testing
5
6 .. _testing_chapter:
7
8 Unit, Integration, and Functional Testing
9 =========================================
10
11 *Unit testing* is, not surprisingly, the act of testing a "unit" in your
12 application.  In this context, a "unit" is often a function or a method of a
13 class instance.  The unit is also referred to as a "unit under test".
14
15 The goal of a single unit test is to test **only** some permutation of the
4ade6d 16 "unit under test".  If you write a unit test that aims to verify the result of
SP 17 a particular codepath through a Python function, you need only be concerned
18 about testing the code that *lives in the function body itself*. If the
19 function accepts a parameter that represents a complex application "domain
20 object" (such as a resource, a database connection, or an SMTP server), the
21 argument provided to this function during a unit test *need not be* and likely
22 *should not be* a "real" implementation object.  For example, although a
23 particular function implementation may accept an argument that represents an
24 SMTP server object, and the function may call a method of this object when the
25 system is operating normally that would result in an email being sent, a unit
26 test of this codepath of the function does *not* need to test that an email is
27 actually sent.  It just needs to make sure that the function calls the method
28 of the object provided as an argument that *would* send an email if the
29 argument happened to be the "real" implementation of an SMTP server object.
6ee49a 30
CM 31 An *integration test*, on the other hand, is a different form of testing in
32 which the interaction between two or more "units" is explicitly tested.
4ade6d 33 Integration tests verify that the components of your application work together.
SP 34 You *might* make sure that an email was actually sent in an integration test.
6ee49a 35
CM 36 A *functional test* is a form of integration test in which the application is
4ade6d 37 run "literally".  You would *have to* make sure that an email was actually sent
SP 38 in a functional test, because it tests your code end to end.
6ee49a 39
4ade6d 40 It is often considered best practice to write each type of tests for any given
SP 41 codebase.  Unit testing often provides the opportunity to obtain better
6ee49a 42 "coverage": it's usually possible to supply a unit under test with arguments
CM 43 and/or an environment which causes *all* of its potential codepaths to be
44 executed.  This is usually not as easy to do with a set of integration or
bfd8d5 45 functional tests, but integration and functional testing provides a measure of
6ee49a 46 assurance that your "units" work together, as they will be expected to when
CM 47 your application is run in production.
48
49 The suggested mechanism for unit and integration testing of a :app:`Pyramid`
50 application is the Python :mod:`unittest` module.  Although this module is
51 named :mod:`unittest`, it is actually capable of driving both unit and
52 integration tests.  A good :mod:`unittest` tutorial is available within `Dive
eb7297 53 Into Python <http://www.diveintopython.net/unit_testing/index.html>`_ by Mark
6ee49a 54 Pilgrim.
CM 55
4ade6d 56 :app:`Pyramid` provides a number of facilities that make unit, integration, and
SP 57 functional tests easier to write.  The facilities become particularly useful
58 when your code calls into :app:`Pyramid`-related framework functions.
6ee49a 59
CM 60 .. index::
61    single: test setup
62    single: test tear down
63    single: unittest
64
65 .. _test_setup_and_teardown:
66
67 Test Set Up and Tear Down
4ade6d 68 -------------------------
6ee49a 69
CM 70 :app:`Pyramid` uses a "global" (actually :term:`thread local`) data structure
a85491 71 to hold two items: the current :term:`request` and the current
6ee49a 72 :term:`application registry`.  These data structures are available via the
CM 73 :func:`pyramid.threadlocal.get_current_request` and
4ade6d 74 :func:`pyramid.threadlocal.get_current_registry` functions, respectively. See
SP 75 :ref:`threadlocals_chapter` for information about these functions and the data
76 structures they return.
6ee49a 77
CM 78 If your code uses these ``get_current_*`` functions or calls :app:`Pyramid`
f52d59 79 code which uses ``get_current_*`` functions, you will need to call
CM 80 :func:`pyramid.testing.setUp` in your test setup and you will need to call
81 :func:`pyramid.testing.tearDown` in your test teardown.
4ade6d 82 :func:`~pyramid.testing.setUp` pushes a registry onto the :term:`thread local`
SP 83 stack, which makes the ``get_current_*`` functions work.  It returns a
f52d59 84 :term:`Configurator` object which can be used to perform extra configuration
CM 85 required by the code under test.  :func:`~pyramid.testing.tearDown` pops the
86 thread local stack.
6ee49a 87
4ade6d 88 Normally when a Configurator is used directly with the ``main`` block of a
SP 89 Pyramid application, it defers performing any "real work" until its ``.commit``
90 method is called (often implicitly by the
91 :meth:`pyramid.config.Configurator.make_wsgi_app` method).  The Configurator
92 returned by :func:`~pyramid.testing.setUp` is an *autocommitting* Configurator,
93 however, which performs all actions implied by methods called on it
94 immediately.  This is more convenient for unit testing purposes than needing to
95 call :meth:`pyramid.config.Configurator.commit` in each test after adding extra
96 configuration statements.
6ee49a 97
f52d59 98 The use of the :func:`~pyramid.testing.setUp` and
4ade6d 99 :func:`~pyramid.testing.tearDown` functions allows you to supply each unit test
SP 100 method in a test case with an environment that has an isolated registry and an
101 isolated request for the duration of a single test.  Here's an example of using
102 this feature:
6ee49a 103
CM 104 .. code-block:: python
105    :linenos:
106
107    import unittest
f52d59 108    from pyramid import testing
6ee49a 109
CM 110    class MyTest(unittest.TestCase):
111        def setUp(self):
f52d59 112            self.config = testing.setUp()
6ee49a 113
CM 114        def tearDown(self):
f52d59 115            testing.tearDown()
6ee49a 116
4ade6d 117 The above will make sure that :func:`~pyramid.threadlocal.get_current_registry`
SP 118 called within a test case method of ``MyTest`` will return the
119 :term:`application registry` associated with the ``config`` Configurator
120 instance.  Each test case method attached to ``MyTest`` will use an isolated
121 registry.
6ee49a 122
f52d59 123 The :func:`~pyramid.testing.setUp` and :func:`~pyramid.testing.tearDown`
4ade6d 124 functions accept various arguments that influence the environment of the test.
SP 125 See the :ref:`testing_module` API for information about the extra arguments
126 supported by these functions.
6ee49a 127
138706 128 If you also want to make :func:`~pyramid.threadlocal.get_current_request`
MR 129 return something other than ``None`` during the course of a single test, you
4ade6d 130 can pass a :term:`request` object into the :func:`pyramid.testing.setUp` within
SP 131 the ``setUp`` method of your test:
6ee49a 132
CM 133 .. code-block:: python
134    :linenos:
135
136    import unittest
137    from pyramid import testing
138
139    class MyTest(unittest.TestCase):
140        def setUp(self):
141            request = testing.DummyRequest()
f52d59 142            self.config = testing.setUp(request=request)
6ee49a 143
CM 144        def tearDown(self):
f52d59 145            testing.tearDown()
6ee49a 146
4ade6d 147 If you pass a :term:`request` object into :func:`pyramid.testing.setUp` within
SP 148 your test case's ``setUp``, any test method attached to the ``MyTest`` test
149 case that directly or indirectly calls
70acd2 150 :func:`~pyramid.threadlocal.get_current_request` will receive the request
e849fe 151 object.  Otherwise, during testing,
4ade6d 152 :func:`~pyramid.threadlocal.get_current_request` will return ``None``. We use a
SP 153 "dummy" request implementation supplied by
154 :class:`pyramid.testing.DummyRequest` because it's easier to construct than a
155 "real" :app:`Pyramid` request object.
6ee49a 156
fbbb20 157 Test setup using a context manager
BS 158 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
159
4ade6d 160 An alternative style of setting up a test configuration is to use the ``with``
SP 161 statement and :func:`pyramid.testing.testConfig` to create a context manager.
162 The context manager will call :func:`pyramid.testing.setUp` before the code
163 under test and :func:`pyramid.testing.tearDown` afterwards.
fbbb20 164
BS 165 This style is useful for small self-contained tests. For example:
166
167 .. code-block:: python
168    :linenos:
169
170    import unittest
171
172    class MyTest(unittest.TestCase):
173
174        def test_my_function(self):
175            from pyramid import testing
176            with testing.testConfig() as config:
177                config.add_route('bar', '/bar/{id}')
178                my_function_which_needs_route_bar()
179
6ee49a 180 What?
CM 181 ~~~~~
182
183 Thread local data structures are always a bit confusing, especially when
184 they're used by frameworks.  Sorry.  So here's a rule of thumb: if you don't
185 *know* whether you're calling code that uses the
70acd2 186 :func:`~pyramid.threadlocal.get_current_registry` or
CM 187 :func:`~pyramid.threadlocal.get_current_request` functions, or you don't care
f52d59 188 about any of this, but you still want to write test code, just always call
CM 189 :func:`pyramid.testing.setUp` in your test's ``setUp`` method and
190 :func:`pyramid.testing.tearDown` in your tests' ``tearDown`` method.  This
4ade6d 191 won't really hurt anything if the application you're testing does not call any
SP 192 ``get_current*`` function.
6ee49a 193
CM 194 .. index::
195    single: pyramid.testing
196    single: Configurator testing API
197
198 Using the ``Configurator`` and ``pyramid.testing`` APIs in Unit Tests
f52d59 199 ---------------------------------------------------------------------
6ee49a 200
3fc77c 201 The ``Configurator`` API and the :mod:`pyramid.testing` module provide a number
6ee49a 202 of functions which can be used during unit testing.  These functions make
CM 203 :term:`configuration declaration` calls to the current :term:`application
204 registry`, but typically register a "stub" or "dummy" feature in place of the
205 "real" feature that the code would call if it was being run normally.
206
207 For example, let's imagine you want to unit test a :app:`Pyramid` view
208 function.
209
210 .. code-block:: python
211    :linenos:
212
99edc5 213    from pyramid.httpexceptions import HTTPForbidden
6ee49a 214
9904b3 215    def view_fn(request):
3bd1fa 216        if request.has_permission('edit'):
a7e625 217            raise HTTPForbidden
9904b3 218        return {'greeting':'hello'}
CM 219
613a7e 220 .. note::
TL 221
222    This code implies that you have defined a renderer imperatively in a
4ade6d 223    relevant :class:`pyramid.config.Configurator` instance, otherwise it would
SP 224    fail when run normally.
613a7e 225
9904b3 226 Without doing anything special during a unit test, the call to
3bd1fa 227 :meth:`~pyramid.request.Request.has_permission` in this view function will
CM 228 always return a ``True`` value.  When a :app:`Pyramid` application starts
4ade6d 229 normally, it will populate an :term:`application registry` using
3bd1fa 230 :term:`configuration declaration` calls made against a :term:`Configurator`.
4ade6d 231 But if this application registry is not created and populated (e.g., by
3bd1fa 232 initializing the configurator with an authorization policy), like when you
CM 233 invoke application code via a unit test, :app:`Pyramid` API functions will tend
234 to either fail or return default results.  So how do you test the branch of the
235 code in this view function that raises
236 :exc:`~pyramid.httpexceptions.HTTPForbidden`?
6ee49a 237
CM 238 The testing API provided by :app:`Pyramid` allows you to simulate various
239 application registry registrations for use under a unit testing framework
240 without needing to invoke the actual application configuration implied by its
9904b3 241 ``main`` function.  For example, if you wanted to test the above ``view_fn``
6ee49a 242 (assuming it lived in the package named ``my.package``), you could write a
CM 243 :class:`unittest.TestCase` that used the testing API.
244
245 .. code-block:: python
246    :linenos:
247
248    import unittest
249    from pyramid import testing
250
251    class MyTest(unittest.TestCase):
252        def setUp(self):
f52d59 253            self.config = testing.setUp()
6ee49a 254
CM 255        def tearDown(self):
f52d59 256            testing.tearDown()
6ee49a 257        
9904b3 258        def test_view_fn_forbidden(self):
99edc5 259            from pyramid.httpexceptions import HTTPForbidden
6ee49a 260            from my.package import view_fn
9904b3 261            self.config.testing_securitypolicy(userid='hank', 
CM 262                                               permissive=False)
6ee49a 263            request = testing.DummyRequest()
9904b3 264            request.context = testing.DummyResource()
a7e625 265            self.assertRaises(HTTPForbidden, view_fn, request)
6ee49a 266
9904b3 267        def test_view_fn_allowed(self):
6ee49a 268            from my.package import view_fn
9904b3 269            self.config.testing_securitypolicy(userid='hank', 
CM 270                                               permissive=True)
6ee49a 271            request = testing.DummyRequest()
9904b3 272            request.context = testing.DummyResource()
6ee49a 273            response = view_fn(request)
9904b3 274            self.assertEqual(response, {'greeting':'hello'})
CM 275            
6ee49a 276 In the above example, we create a ``MyTest`` test case that inherits from
d4683c 277 :class:`unittest.TestCase`.  If it's in our :app:`Pyramid` application, it will
e976a6 278 be found when ``py.test`` is run.  It has two test methods.
6ee49a 279
9904b3 280 The first test method, ``test_view_fn_forbidden`` tests the ``view_fn`` when
4ade6d 281 the authentication policy forbids the current user the ``edit`` permission. Its
SP 282 third line registers a "dummy" "non-permissive" authorization policy using the
283 :meth:`~pyramid.config.Configurator.testing_securitypolicy` method, which is a
284 special helper method for unit testing.
6ee49a 285
3bd1fa 286 We then create a :class:`pyramid.testing.DummyRequest` object which simulates a
CM 287 WebOb request object API.  A :class:`pyramid.testing.DummyRequest` is a request
288 object that requires less setup than a "real" :app:`Pyramid` request.  We call
289 the function being tested with the manufactured request.  When the function is
290 called, :meth:`pyramid.request.Request.has_permission` will call the "dummy"
291 authentication policy we've registered through
d4683c 292 :meth:`~pyramid.config.Configurator.testing_securitypolicy`, which denies
TL 293 access.  We check that the view function raises a
294 :exc:`~pyramid.httpexceptions.HTTPForbidden` error.
6ee49a 295
cdd8d4 296 The second test method, named ``test_view_fn_allowed``, tests the alternate
9904b3 297 case, where the authentication policy allows access.  Notice that we pass
4ade6d 298 different values to :meth:`~pyramid.config.Configurator.testing_securitypolicy`
SP 299 to obtain this result.  We assert at the end of this that the view function
300 returns a value.
6ee49a 301
f52d59 302 Note that the test calls the :func:`pyramid.testing.setUp` function in its
CM 303 ``setUp`` method and the :func:`pyramid.testing.tearDown` function in its
4ade6d 304 ``tearDown`` method.  We assign the result of :func:`pyramid.testing.setUp` as
SP 305 ``config`` on the unittest class.  This is a :term:`Configurator` object and
306 all methods of the configurator can be called as necessary within tests. If you
307 use any of the :class:`~pyramid.config.Configurator` APIs during testing, be
308 sure to use this pattern in your test case's ``setUp`` and ``tearDown``; these
309 methods make sure you're using a "fresh" :term:`application registry` per test
310 run.
6ee49a 311
4ade6d 312 See the :ref:`testing_module` chapter for the entire :app:`Pyramid`-specific
6ee49a 313 testing API.  This chapter describes APIs for registering a security policy,
4ade6d 314 registering resources at paths, registering event listeners, registering views
SP 315 and view permissions, and classes representing "dummy" implementations of a
316 request and a resource.
6ee49a 317
2033ee 318 .. seealso::
SP 319
320     See also the various methods of the :term:`Configurator` documented in
321     :ref:`configuration_module` that begin with the ``testing_`` prefix.
6ee49a 322
CM 323 .. index::
324    single: integration tests
325
326 .. _integration_tests:
327
328 Creating Integration Tests
329 --------------------------
330
331 In :app:`Pyramid`, a *unit test* typically relies on "mock" or "dummy"
138706 332 implementations to give the code under test enough context to run.
6ee49a 333
CM 334 "Integration testing" implies another sort of testing.  In the context of a
138706 335 :app:`Pyramid` integration test, the test logic exercises the functionality of
MR 336 the code under test *and* its integration with the rest of the :app:`Pyramid`
6ee49a 337 framework.
CM 338
138706 339 Creating an integration test for a :app:`Pyramid` application usually means
MR 340 invoking the application's ``includeme`` function via
341 :meth:`pyramid.config.Configurator.include` within the test's setup code.  This
342 causes the entire :app:`Pyramid` environment to be set up, simulating what
343 happens when your application is run "for real".  This is a heavy-hammer way of
344 making sure that your tests have enough context to run properly, and tests your
345 code's integration with the rest of :app:`Pyramid`.
6ee49a 346
138706 347 .. seealso::
6ee49a 348
9c94e1 349    See also :ref:`including_configuration`
6ee49a 350
138706 351 Writing unit tests that use the :class:`~pyramid.config.Configurator` API to
MR 352 set up the right "mock" registrations is often preferred to creating
353 integration tests.  Unit tests will run faster (because they do less for each
354 test) and are usually easier to reason about.
6ee49a 355
CM 356 .. index::
357    single: functional tests
358
359 .. _functional_tests:
360
361 Creating Functional Tests
362 -------------------------
363
364 Functional tests test your literal application.
365
138706 366 In Pyramid, functional tests are typically written using the :term:`WebTest`
MR 367 package, which provides APIs for invoking HTTP(S) requests to your application.
e976a6 368 We also like ``py.test`` and ``pytest-cov`` to provide simple testing and
SP 369 coverage reports.
6ee49a 370
e976a6 371 Regardless of which testing :term:`package` you use, be sure to add a
SP 372 ``tests_require`` dependency on that package to your application's ``setup.py``
fb77c9 373 file. Using the project ``myproject`` generated by the starter cookiecutter as
e976a6 374 described in :doc:`project`, we would insert the following code immediately
fb77c9 375 following the ``requires`` block in the file ``myproject/setup.py``.
6ee49a 376
fb77c9 377 .. literalinclude:: myproject/setup.py
e976a6 378     :language: python
bb079f 379     :lines: 11-23
SP 380     :lineno-match:
381     :emphasize-lines: 9-
6ee49a 382
312aa1 383 Remember to change the dependency.
SP 384
fb77c9 385 .. literalinclude:: myproject/setup.py
e976a6 386     :language: python
bb079f 387     :lines: 42-46
SP 388     :lineno-match:
e976a6 389     :emphasize-lines: 2-4
312aa1 390
e976a6 391 As always, whenever you change your dependencies, make sure to run the correct
SP 392 ``pip install -e`` command.
312aa1 393
SP 394 .. code-block:: bash
395
e976a6 396     $VENV/bin/pip install -e ".[testing]"
312aa1 397
SP 398 In your ``MyPackage`` project, your :term:`package` is named ``myproject``
399 which contains a ``views`` module, which in turn contains a :term:`view`
400 function ``my_view`` that returns an HTML body when the root URL is invoked:
6ee49a 401
fb77c9 402    .. literalinclude:: myproject/myproject/views.py
138706 403       :linenos:
MR 404       :language: python
6ee49a 405
312aa1 406 The following example functional test demonstrates invoking the above
4ade6d 407 :term:`view`:
138706 408
fb77c9 409    .. literalinclude:: myproject/myproject/tests.py
138706 410       :linenos:
MR 411       :pyobject: FunctionalTests
412       :language: python
413
414 When this test is run, each test method creates a "real" :term:`WSGI`
415 application using the ``main`` function in your ``myproject.__init__`` module,
416 using :term:`WebTest` to wrap that WSGI application.  It assigns the result to
312aa1 417 ``self.testapp``.  In the test named ``test_root``, the ``TestApp``'s ``GET``
138706 418 method is used to invoke the root URL.  Finally, an assertion is made that the
312aa1 419 returned HTML contains the text ``Pyramid``.
6ee49a 420
4ade6d 421 See the :term:`WebTest` documentation for further information about the methods
SP 422 available to a :class:`webtest.app.TestApp` instance.