summaryrefslogtreecommitdiff
path: root/docs/narr/unittesting.rst
blob: 885d7be366fcd94c50add9ab6497239670012eee (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
.. _unittesting_chapter:

Unit Testing
============

The suggested mechanism for unit testing :mod:`repoze.bfg`
applications is the Python ``unittest`` module.  :mod:`repoze.bfg`
provides a number of facilities that make unit tests easier to write.
The facilities become particularly useful when your code calls into
:mod:`repoze.bfg` -related framework functions.

Using the ``repoze.bfg.testing`` API
------------------------------------

The ``repoze.bfg.testing`` module provides a number of functions which
can be used during unit testing.  For example, let's imagine you want
to unit test a :mod:`repoze.bfg` view function.

.. code-block:: python
   :linenos:

   def view_fn(context, request):
       from repoze.bfg.chameleon_zpt import render_template_to_response
       if 'say' in request.params:
           return render_template_to_response('templates/submitted.pt',
                                               say=request.params['say'])
       return render_template_to_response('templates/show.pt', say='Hello')

Without invoking any ZCML or using the testing API, an attempt to run
this view function will result in an error.  When a :mod:`repoze.bfg`
application starts normally, it will create an application registry
from the information it finds in the application's ``configure.zcml``
file.  But if this application registry is not created and populated
(e.g. with ``view`` declarations in 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
various application registry registrations for use under a unit
testing framework without needing to invoke the actual application
ZCML configuration.  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.

.. code-block:: python
   :linenos:

   import unittest
   from repoze.bfg import testing

   class MyTest(unittest.TestCase):
       def setUp(self):
           testing.setUp()

       def tearDown(self):
           testing.tearDown()
       
       def test_view_fn_not_submitted(self):
           from my.package import view_fn
           renderer = testing.registerTemplateRenderer('templates/show.pt')
           context = testing.DummyModel()
           request = testing.DummyRequest()
           response = view_fn(context, request)
           renderer.assert_(say='Hello')

       def test_view_fn_submitted(self):
           from my.package import view_fn
           renderer = testing.registerTemplateRenderer('templates/submitted.pt')
           context = testing.DummyModel()
           request = testing.DummyRequest()
           request.params['say'] = 'Yo'
           response = view_fn(context, request)
           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`
application, it will be found when ``setup.py test`` is run.  It has
two test methods.

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
``registerTemplateRenderer`` function (a ``repoze.bfg.testing`` API);
this function returns a DummyTemplateRenderer instance which we hang
on to for later.  We then create a ``DummyRequest`` object (it
simulates a WebOb request object), and we create a ``DummyModel``
context object.  We call the function being tested with the
manufactured context and 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).

The second test method, named ``test_view_fn_submitted`` tests the
alternate case, where the ``say`` form value has already been set in
the request and performs a similar template registration and
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.

See the :ref:`testing_module` chapter for the entire :mod:`repoze.bfg`
-specific testing API.  This chapter describes APIs for registering a
security policy, registering models at paths, registering event
listeners, registering views and view permissions, and classes
representing "dummy" implementations of a request and a model.