diff options
Diffstat (limited to 'docs/narr')
| -rw-r--r-- | docs/narr/MyProject/myproject/run.py | 2 | ||||
| -rw-r--r-- | docs/narr/MyProject/myproject/tests.py | 6 | ||||
| -rw-r--r-- | docs/narr/configuration.rst | 67 | ||||
| -rw-r--r-- | docs/narr/project.rst | 2 | ||||
| -rw-r--r-- | docs/narr/threadlocals.rst | 14 | ||||
| -rw-r--r-- | docs/narr/unittesting.rst | 122 |
6 files changed, 157 insertions, 56 deletions
diff --git a/docs/narr/MyProject/myproject/run.py b/docs/narr/MyProject/myproject/run.py index 0d7647aa7..6a3671c1e 100644 --- a/docs/narr/MyProject/myproject/run.py +++ b/docs/narr/MyProject/myproject/run.py @@ -6,8 +6,10 @@ def app(global_config, **settings): is usually called by the PasteDeploy framework during ``paster serve``""" config = Configurator(root_factory=get_root, settings=settings) + config.begin() zcml_file = settings.get('configure_zcml', 'configure.zcml') config.load_zcml(zcml_file) + config.end() return config.make_wsgi_app() diff --git a/docs/narr/MyProject/myproject/tests.py b/docs/narr/MyProject/myproject/tests.py index 7c3caac74..498bd96e8 100644 --- a/docs/narr/MyProject/myproject/tests.py +++ b/docs/narr/MyProject/myproject/tests.py @@ -1,13 +1,15 @@ import unittest +from repoze.bfg.configuration import Configurator from repoze.bfg import testing class ViewTests(unittest.TestCase): def setUp(self): - testing.setUp() + self.config = Configurator() + self.config.begin() def tearDown(self): - testing.tearDown() + self.config.end() def test_my_view(self): from myproject.views import my_view diff --git a/docs/narr/configuration.rst b/docs/narr/configuration.rst index 9e7c75005..367df2a2d 100644 --- a/docs/narr/configuration.rst +++ b/docs/narr/configuration.rst @@ -86,8 +86,10 @@ imperatively: if __name__ == '__main__': config = Configurator() + config.begin() config.add_view(hello_world) config.add_view(goodbye_world, name='goodbye') + config.end() app = config.make_wsgi_app() serve(app) @@ -274,12 +276,17 @@ imports and function definitions is placed within the confines of an if __name__ == '__main__': config = Configurator() + config.begin() config.add_view(hello_world) config.add_view(goodbye_world, name='goodbye') + config.end() app = config.make_wsgi_app() simple_server.make_server('', 8080, app).serve_forever() -Let's break this down this piece-by-piece: +Let's break this down this piece-by-piece. + +Configurator Construction +~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: python :linenos: @@ -315,6 +322,27 @@ this particular :mod:`repoze.bfg` application. registry object being configured by a ``Configurator`` is available as its ``registry`` attribute. +Beginning Configuration +~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + :linenos: + + config.begin() + +The ``begin`` method of a Configurator tells the the system that +application configuration has begun. In particular, this causes the +:term:`application registry` associated with this configurator to +become the "current" application registry, meaning that code which +attempts to use the application registry :term:`thread local` will +obtain the registry associated with the configurator. This is an +explicit step because it's sometimes convenient to use a configurator +without causing the registry associated with the configurator to +become "current". + +Adding Configuration +~~~~~~~~~~~~~~~~~~~~ + .. code-block:: python :linenos: @@ -411,6 +439,22 @@ the best view configuration for any request, the ``goodbye_world`` view callable will be used when the URL contains path information that ends with ``/goodbye``. +Ending Configuration +~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + :linenos: + + config.end() + +The ``end`` method of a Configurator tells the the system that +application configuration has ended. It is the inverse of +``config.begin``. In particular, this causes the :term:`application +registry` associated with this configurator to no longer be the +"current" application registry, meaning that code which attempts to +use the application registry :term:`thread local` will no longer +obtain the registry associated with the configurator. + WSGI Application Creation ~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -419,13 +463,14 @@ WSGI Application Creation app = config.make_wsgi_app() -After configuring views, the script creates a WSGI *application* via -the ``config.make_wsgi_app`` method. A call to ``make_wsgi_app`` -implies that all configuration is finished (meaning all method calls -to the configurator which set up views, and various other -configuration settings have been performed). The ``make_wsgi_app`` -method returns a :term:`WSGI` application object that can be used by -any WSGI server to present an application to a requestor. +After configuring views and ending configuration, the script creates a +WSGI *application* via the ``config.make_wsgi_app`` method. A call to +``make_wsgi_app`` implies that all configuration is finished (meaning +all method calls to the configurator which set up views, and various +other configuration settings have been performed). The +``make_wsgi_app`` method returns a :term:`WSGI` application object +that can be used by any WSGI server to present an application to a +requestor. The :mod:`repoze.bfg` application object, in particular, is an instance of the ``repoze.bfg.router.Router`` class. It has a @@ -510,7 +555,9 @@ In a file named ``helloworld.py``: if __name__ == '__main__': config = Configurator() + config.begin() config.load_zcml('configure.zcml) + config.end() app = config.make_wsgi_app() serve(app) @@ -547,8 +594,10 @@ within the ``if __name__ == '__main__'`` section of ``helloworld.py``: if __name__ == '__main__': config = Configurator() + config.begin() config.add_view(hello_world) config.add_view(goodbye_world, name='goodbye') + config.end() app = config.make_wsgi_app() simple_server.make_server('', 8080, app).serve_forever() @@ -563,7 +612,9 @@ reads as: if __name__ == '__main__': config = Configurator() + config.begin() config.load_zcml('configure.zcml') + config.end() app = config.make_wsgi_app() simple_server.make_server('', 8080, app).serve_forever() diff --git a/docs/narr/project.rst b/docs/narr/project.rst index 81e570e36..16879b69b 100644 --- a/docs/narr/project.rst +++ b/docs/narr/project.rst @@ -724,7 +724,7 @@ without the PasteDeploy configuration file: #. Line 2 imports the ``get_root`` function from :mod:`myproject.models` that we use later. -#. Lines 4-11 define a function that returns a :mod:`repoze.bfg` +#. Lines 4-13 define a function that returns a :mod:`repoze.bfg` WSGI application. This function is meant to be called by the :term:`PasteDeploy` framework as a result of running ``paster serve``. diff --git a/docs/narr/threadlocals.rst b/docs/narr/threadlocals.rst index 476b179ec..b97d9e4f2 100644 --- a/docs/narr/threadlocals.rst +++ b/docs/narr/threadlocals.rst @@ -69,10 +69,11 @@ defined entirely by the behavior of a repoze.bfg :term:`Router`. However, during unit testing, no Router code is ever invoked, and the definition of "current" is defined by the boundary between calls to -the ``repoze.bfg.testing.setUp`` and ``repoze.bfg.testing.tearDown``. -These functions push and pop the threadlocal stack when the system is -under test. See :ref:`test_setup_and_teardown` for the definitions of -these functions. +the ``begin`` and ``end`` methods of a :term:`Configurator` (or, +pre-1.2a6, between calls to the ``repoze.bfg.testing.setUp`` and +``repoze.bfg.testing.tearDown`` functions). These functions push and +pop the threadlocal stack when the system is under test. See +:ref:`test_setup_and_teardown` for the definitions of these functions. Scripts which use :mod:`repoze.bfg` machinery but never actually start a WSGI server or receive requests via HTTP such as scripts which use @@ -142,7 +143,8 @@ and the hack that uses ``get_current_request`` is removed. This would be an appropriate place to use the ``get_current_request`` function. Use of the ``get_current_registry`` function should be limited to -testing scenarios. The registry created by -``repoze.bfg.testing.setUp`` when you do not pass one in is available +testing scenarios. The registry made current by use of a +Configurator's ``begin`` method during a test (or pre-1.2a6, via +``repoze.bfg.testing.setUp``) when you do not pass one in is available to you via this API. 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 |
