diff options
| author | Chris McDonough <chrism@agendaless.com> | 2009-12-24 00:25:11 +0000 |
|---|---|---|
| committer | Chris McDonough <chrism@agendaless.com> | 2009-12-24 00:25:11 +0000 |
| commit | f0c7d07c9532c51f7eb354257ff3563ebba91cf2 (patch) | |
| tree | 7596cbb5f3916801786e14ec367c826db6da1e57 /docs | |
| parent | dae1d57789e60f783497e10e089e4e0669c42b79 (diff) | |
| download | pyramid-f0c7d07c9532c51f7eb354257ff3563ebba91cf2.tar.gz pyramid-f0c7d07c9532c51f7eb354257ff3563ebba91cf2.tar.bz2 pyramid-f0c7d07c9532c51f7eb354257ff3563ebba91cf2.zip | |
Roles.
Diffstat (limited to 'docs')
| -rw-r--r-- | docs/narr/unittesting.rst | 134 |
1 files changed, 73 insertions, 61 deletions
diff --git a/docs/narr/unittesting.rst b/docs/narr/unittesting.rst index e49221b78..316142c08 100644 --- a/docs/narr/unittesting.rst +++ b/docs/narr/unittesting.rst @@ -40,10 +40,10 @@ measure of assurance that your "units" work together, as they will be expected to when your application is run in production. The suggested mechanism for unit and integration testing of a -:mod:`repoze.bfg` application is the Python ``unittest`` module. -Although this module is named ``unittest``, it is actually capable of -driving both unit and integration tests. A good ``unittest`` tutorial -is available within `Dive Into Python +:mod:`repoze.bfg` application is the Python :mod:`unittest` module. +Although this module is named :mod:`unittest`, it is actually capable +of driving both unit and integration tests. A good :mod:`unittest` +tutorial is available within `Dive Into Python <http://diveintopython.org/unit_testing/index.html>`_ by Mark Pilgrim. :mod:`repoze.bfg` provides a number of facilities that make unit and @@ -59,8 +59,8 @@ Test Set Up and Tear Down :mod:`repoze.bfg` uses a "global" (actually :term:`thread local`) data structure to hold on to two items: the current :term:`request` and the current :term:`application registry`. These data structures are -available via the ``repoze.bfg.threadlocal.get_current_request`` and -``repoze.bfg.threadlocal.get_current_registry`` functions, +available via the :func:`repoze.bfg.threadlocal.get_current_request` +and :func:`repoze.bfg.threadlocal.get_current_registry` functions, respectively. See :ref:`threadlocals_chapter` for information about these functions and the data structures they return. @@ -91,20 +91,23 @@ of a single test. Here's an example of using this feature: self.config.end() The above will make sure that -``repoze.bfg.threadlocal.get_current_registry`` will return the +:func:`repoze.bfg.threadlocal.get_current_registry` will return the :term:`application registry` associated with the ``config`` -Configurator instance when ``get_current_registry`` is called in a +Configurator instance when +:func:`repoze.bfg.threadlocal.get_current_registry` is called in a test case method attached to ``MyTest``. Each test case method attached to ``MyTest`` will use an isolated registry. -The ``begin`` method of a Configurator accepts various arguments that -influence the code run during the test. See the -:ref:`configuration_module` chapter for information about the API of a -:term:`Configurator`, including its ``begin`` and ``end`` methods. +The :meth:`repoze.bfg.configuration.Configurator.begin` method accepts +various arguments that influence the code run during the test. See +the :ref:`configuration_module` chapter for information about the API +of a :term:`Configurator`, including its ``begin`` and ``end`` +methods. -If you also want to make ``repoze.bfg.get_current_registry`` return -something other than ``None`` during the course of a single test, you -can pass a :term:`request` object into the ``begin`` method of the +If you also want to make :func:`repoze.bfg.get_current_registry` +return something other than ``None`` during the course of a single +test, you can pass a :term:`request` object into the +:meth:`repoze.bfg.configuration.Configurator.begin` method of the Configurator within the ``setUp`` method of your test: .. code-block:: python @@ -123,15 +126,15 @@ Configurator within the ``setUp`` method of your test: def tearDown(self): self.config.end() -If you pass a term:`Request` object into the ``begin`` method of the +If you pass a term:`request` object into the ``begin`` method of the configurator within your test case's ``setUp``, any test method attached to the ``MyTest`` test case that directly or indirectly calls -``get_current_request`` will receive the request you passed into the -``begin`` method. Otherwise, during testing, ``get_current_request`` -will return ``None``. We use a "dummy" request implementation -supplied by ``repoze.bfg.testing.DummyRequest`` because it's easier to -construct than a "real" :mod:`repoze.bfg` request object. See -:ref:`testing_module` for documentation about a DummyRequest. +:func:`repoze.bfg.threadlocal.get_current_request` will receive the +request you passed into the ``begin`` method. Otherwise, during +testing, :func:`repoze.bfg.threadlocal.get_current_request` will +return ``None``. We use a "dummy" request implementation supplied by +:class:`repoze.bfg.testing.DummyRequest` because it's easier to +construct than a "real" :mod:`repoze.bfg` request object. What? ~~~~~ @@ -139,9 +142,10 @@ What? Thread local data structures are always a bit confusing, especially when used by frameworks. Sorry. So here's a rule of thumb: if you don't *know* whether you're calling code that uses the -``get_current_registry`` or ``get_current_request`` functions, or you +:func:`repoze.bfg.threadlocal.get_current_registry` or +:func:`repoze.bfg.threadlocal.get_current_request` functions, or you don't care about any of this, but you still want to write test code, -just always create a configurator instance and call its ``begin`` +just always create a Configurator instance and call its ``begin`` method within the ``setUp`` of a unit test, then subsequently call its ``end`` method in the test's ``tearDown``. This won't really hurt anything if the application you're testing does not call any @@ -177,17 +181,18 @@ a :term:`application registry` using :term:`configuration declaration` calls made against a :term:`Configurator` (sometimes deferring to the application's ``configure.zcml`` :term:`ZCML` file via ``load_zcml``). But if this application registry is not created and populated -(e.g. with an ``add_view`` :term:`configuration declaration` or -``view`` declarations in :term:`ZCML`), like when you invoke -application code via a unit test, :mod:`repoze.bfg` API functions will -tend to fail. +(e.g. with an :meth:`repoze.bfg.configuration.Configurator.add_view` +:term:`configuration declaration` or ``view`` declarations in +:term:`ZCML`), like when you invoke application code via a unit test, +:mod:`repoze.bfg` API functions will tend to fail. -The testing API provided by ``repoze.bfg`` allows you to simulate +The testing API provided by :mod:`repoze.bfg` allows you to simulate various application registry registrations for use under a unit testing framework without needing to invoke the actual application configuration implied by its ``run.py``. For example, if you wanted -to test the above ``view_fn`` (assuming it lived in ``my.package``), -you could write a unittest TestCase that used the testing API. +to test the above ``view_fn`` (assuming it lived in the package named +``my.package``), you could write a :class:`unittest.TestCase` that +used the testing API. .. code-block:: python :linenos: @@ -220,7 +225,7 @@ you could write a unittest TestCase that used the testing API. renderer.assert_(say='Yo') In the above example, we create a ``MyTest`` test case that inherits -from ``unittest.TestCase``. If it's in our :mod:`repoze.bfg` +from :mod:`unittest.TestCase`. If it's in our :mod:`repoze.bfg` application, it will be found when ``setup.py test`` is run. It has two test methods. @@ -228,25 +233,29 @@ The first test method, ``test_view_fn_not_submitted`` tests the ``view_fn`` function in the case that no "form" values (represented by request.params) have been submitted. Its first line registers a "dummy template renderer" named ``templates/show.pt`` via the -``testing_add_template`` method of a :term:`Configurator`; this method -returns a ``DummyTemplateRenderer`` instance which we hang on to for -later. - -We then create a ``DummyRequest`` object which simulates a WebOb -request object using the ``repoze.bfg.testing.DummyRequest`` API. A -``DummyRequest`` is a request object that requires less setup than a -"real" :mod:`repoze.bfg` request. We call the function being tested -with the manufactured request. When the function is called, -``render_template_to_response`` will call the "dummy" template -renderer object instead of the real template renderer object. When -the dummy renderer is called, it will set attributes on itself -corresponding to the non-path keyword arguments provided to the -``render_template_to_response`` function. We check that the ``say`` -parameter sent into the template rendering function was ``Hello`` in -this specific example. The ``assert_`` method of the renderer we've -created will raise an ``AssertionError`` if the value passed to the -renderer as ``say`` does not equal ``Hello`` (any number of keyword -arguments are supported). +:meth:`repoze.bfg.configuration.Configurator.testing_add_template` +method; this method returns a +:class:`repoze.bfg.testing.DummyTemplateRenderer` instance which we +hang on to for later. + +We then create a :class:`repoze.bfg.testing.DummyRequest` object which +simulates a WebOb request object API. A +:class:`repoze.bfg.testing.DummyRequest` is a request object that +requires less setup than a "real" :mod:`repoze.bfg` request. We call +the function being tested with the manufactured request. When the +function is called, +:func:`repoze.bfg.chameleon_zpt.render_template_to_response` will call +the "dummy" template renderer object instead of the real template +renderer object. When the dummy renderer is called, it will set +attributes on itself corresponding to the non-path keyword arguments +provided to the +:func:`repoze.bfg.chameleon_zpt.render_template_to_response` function. +We check that the ``say`` parameter sent into the template rendering +function was ``Hello`` in this specific example. The ``assert_`` +method of the renderer we've created will raise an +:exc:`AssertionError` if the value passed to the renderer as ``say`` +does not equal ``Hello`` (any number of keyword arguments are +supported). The second test method, named ``test_view_fn_submitted`` tests the alternate case, where the ``say`` form value has already been set in @@ -255,12 +264,14 @@ assertion. We assert at the end of this that the renderer's ``say`` attribute is ``Yo``, as this is what is expected of the view function in the branch it's testing. -Note that the test calls the ``begin`` method of a -:term:`Configurator` in its ``setUp`` method and the ``end`` method of -the same in its ``tearDown`` method. If you use any of the -``Configurator`` APIs during testing, be sure to use this pattern in -your test setUp and tearDown; these methods make sure you're using a -"fresh" :term:`application registry` per test run. +Note that the test calls the +:meth:`repoze.bfg.configuration.Configurator.begin` method in its +``setUp`` method and the ``end`` method of the same in its +``tearDown`` method. If you use any of the +:class:`repoze.bfg.configuration.Configurator` APIs during testing, be +sure to use this pattern in your test case's ``setUp`` and +``tearDown``; these methods make sure you're using a "fresh" +:term:`application registry` per test run. See the :ref:`testing_module` chapter for the entire :mod:`repoze.bfg` -specific testing API. This chapter describes APIs for registering a @@ -337,9 +348,10 @@ environment. str(len(body)))) Unless you cannot avoid it, you should prefer writing unit tests that -use the :term:`Configurator` API to set up the right "mock" -registrations rather than creating an integration test. Unit tests -will run faster (because they do less for each test) and the result of -a unit test is usually easier to make assertions about. +use the :class:`repoze.bfg.configuration,Configurator` API to set up +the right "mock" registrations rather than creating an integration +test. Unit tests will run faster (because they do less for each test) +and the result of a unit test is usually easier to make assertions +about. |
