diff options
| author | Chris McDonough <chrism@agendaless.com> | 2009-12-17 16:00:02 +0000 |
|---|---|---|
| committer | Chris McDonough <chrism@agendaless.com> | 2009-12-17 16:00:02 +0000 |
| commit | bc857e7e6e71a4001f03c608a18bac7dab36ccff (patch) | |
| tree | a34b0b761a92791bb60b2fbfafabe79e4a675682 /docs/narr/unittesting.rst | |
| parent | 9d73300fcef0c0cd4af9c439a900d15fa4651914 (diff) | |
| download | pyramid-bc857e7e6e71a4001f03c608a18bac7dab36ccff.tar.gz pyramid-bc857e7e6e71a4001f03c608a18bac7dab36ccff.tar.bz2 pyramid-bc857e7e6e71a4001f03c608a18bac7dab36ccff.zip | |
Features
--------
- The ``Configurator`` object now has two new methods: ``begin`` and
``end``. The ``begin`` method is meant to be called before any
"configuration" begins (e.g. before ``add_view``, et. al are
called). The ``end`` method is meant to be called after all
"configuration" is complete.
Previously, before there was imperative configuration at all (1.1
and prior), configuration begin and end was invariably implied by
the process of loading a ZCML file. When a ZCML load happened, the
threadlocal data structure containing the request and registry was
modified before the load, and torn down after the load, making sure
that all framework code that needed ``get_current_registry`` for the
duration of the ZCML load was satisfied.
Some API methods called during imperative configuration, (such as
``Configurator.add_view`` when a renderer is involved) end up for
historical reasons calling ``get_current_registry``. However, in
1.2a5 and below, the Configurator supplied no functionality that
allowed people to make sure that ``get_current_registry`` returned
the registry implied by the configurator being used. ``begin`` now
serves this purpose. Inversely, ``end`` pops the thread local
stack, undoing the actions of ``begin``.
We make this boundary explicit to reduce the potential for confusion
when the configurator is used in different circumstances (e.g. in
unit tests and app code vs. just in initial app setup).
Existing code written for 1.2a1-1.2a5 which does not call ``begin``
or ``end`` continues to work in the same manner it did before. It
is however suggested that this code be changed to call ``begin`` and
``end`` to reduce the potential for confusion in the future.
- All ``paster`` templates which generate an application skeleton now
make use of the new ``begin`` and ``end`` methods of the
Configurator they use in their respective copies of ``run.py`` and
``tests.py``.
Documentation
-------------
- All documentation that makes use of a ``Configurator`` object to do
application setup and test setup now makes use of the new ``begin``
and ``end`` methods of the configurator.
Bug Fixes
---------
- When a ``repoze.bfg.exceptions.NotFound`` or
``repoze.bfg.exceptions.Forbidden`` *class* (as opposed to instance)
was raised as an exception within a root factory (or route root
factory), the exception would not be caught properly by the
``repoze.bfg.`` Router and it would propagate to up the call stack,
as opposed to rendering the not found view or the forbidden view as
would have been expected.
Diffstat (limited to 'docs/narr/unittesting.rst')
| -rw-r--r-- | docs/narr/unittesting.rst | 122 |
1 files changed, 83 insertions, 39 deletions
diff --git a/docs/narr/unittesting.rst b/docs/narr/unittesting.rst index cc8ab6e32..790c0577d 100644 --- a/docs/narr/unittesting.rst +++ b/docs/narr/unittesting.rst @@ -56,7 +56,7 @@ functions. Test Set Up and Tear Down -------------------------- -:mod:`repoze.bfg` uses a "global" (actually thread-local) data +: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 @@ -65,42 +65,84 @@ respectively. See :ref:`threadlocals_chapter` for information about these functions and the data structures they return. If your code uses these ``get_current_*`` functions or calls -:mod:`repoze.bfg` code which uses the ``get_current_*`` functions, you -will need to use the ``repoze.bfg.testing.setUp`` and -``repoze.bfg.testing.tearDown`` functions within the ``setUp`` and -``tearDown`` methods of your unit tests, respectively. +:mod:`repoze.bfg` code which uses ``get_current_*`` functions, you +will need to construct at :term:`Configurator` and call its ``begin`` +method within the ``setUp`` method of your unit test and call the same +configurator's ``end`` method within the ``tearDown`` method of your +unit test. -The ``repoze.bfg.testing.setUp`` and ``repoze.bfg.testing.tearDown`` -functions allow you to supply a unit test with an environment that has -a default registry and a default request for the duration of a single -test. Here's an example of using both: +The use of a Configurator and its ``begin`` and ``end`` methods allows +you to supply each unit test method in a test case with an environment +that has a isolated registry and an isolated request for the duration +of a single test. Here's an example of using this feature: .. code-block:: python :linenos: import unittest - from repoze.bfg import testing + from repoze.bfg.configuration import Configurator + + class MyTest(unittest.TestCase): + def setUp(self): + self.config = Configurator() + self.config.begin() + + def tearDown(self): + self.config.end() + +The above will make sure that +``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 +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. + +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 +Configurator within the ``setUp`` method of your test: + +.. code-block:: python + :linenos: + + import unittest + from repoze.bfg.configuration import Configurator + from repoze.bfg.request import Request class MyTest(unittest.TestCase): def setUp(self): - testing.setUp() + self.config = Configurator() + request = Request() + self.config.begin(request=request) def tearDown(self): - testing.tearDown() - -If you don't *know* whether you're calling code that uses these -functions, a rule of thumb applies: just always use the -``repoze.bfg.testing.setUp`` and ``repoze.bfg.testing.tearDown`` -functions in the ``setUp`` and ``tearDown`` respectively of unit tests -that test :mod:`repoze.bfg` application code, unless it's obvious -you're not calling any :mod:`repoze.bfg` APIs which might make use of -the any "current" global. - -The ``repoze.bfg.testing.setUp`` and ``repoze.bfg.testing.tearDown`` -functions accept various arguments that influence the code run during -the test. See the :ref:`testing_module` chapter for information about -the APIs of ``repoze.bfg.testing.setUp`` and -``repoze.bfg.testing.tearDown``. + self.config.end() + +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``. + +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 +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`` +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 +``get_current*`` function. Using the ``repoze.bfg.testing`` API in Unit Tests -------------------------------------------------- @@ -148,14 +190,16 @@ you could write a unittest TestCase that used the testing API. :linenos: import unittest + from repoze.bfg.configuration import Configurator from repoze.bfg import testing class MyTest(unittest.TestCase): def setUp(self): - testing.setUp() + self.config = Configurator() + self.config.begin() def tearDown(self): - testing.tearDown() + self.config.end() def test_view_fn_not_submitted(self): from my.package import view_fn @@ -204,12 +248,12 @@ 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 ``repoze.bfg.testing.setUp`` function in -its ``setUp`` method and the ``repoze.bfg.testing.tearDown`` function -in its ``tearDown`` method. Use of this pattern is required to -perform cleanup between the test runs. If you use any of the testing -API, be sure to call ``repoze.bfg.testing.setUp`` in the test setup -and ``repoze.bfg.testing.tearDown`` in the test teardown. +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 +``repoze.bfg.testing`` APIs, 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. See the :ref:`testing_module` chapter for the entire :mod:`repoze.bfg` -specific testing API. This chapter describes APIs for registering a @@ -251,6 +295,7 @@ environment. import unittest + from repoze.bfg.configuration import Configurator from repoze.bfg import testing class ViewIntegrationTests(unittest.TestCase): @@ -259,15 +304,14 @@ environment. registrations your application declares in its configure.zcml (including dependent registrations for repoze.bfg itself). """ - from repoze.bfg.configuration import Configurator import myapp - configurator = Configurator(package=myapp) - configurator.load_zcml('myapp:configure.zcml') - testing.setUp(registry=configurator.registry) + self.config = Configurator(package=myapp) + self.config.begin() + self.config.load_zcml('myapp:configure.zcml') def tearDown(self): """ Clear out the application registry """ - testing.tearDown() + self.config.end() def test_my_view(self): from myapp.views import my_view |
