1Testing Applications with Paste
2+++++++++++++++++++++++++++++++
3
4:author: Ian Bicking <ianb@colorstudy.com>
5:revision: $Rev$
6:date: $LastChangedDate$
7
8.. contents::
9
10Introduction
11============
12
13Paste includes functionality for testing your application in a
14convenient manner.  These facilities are quite young, and feedback is
15invited.  Feedback and discussion should take place on the
16`Paste-users list
17<http://groups.google.com/group/paste-users>`_.
18
19These facilities let you test your Paste and WSGI-based applications
20easily and without a server.
21
22.. include:: include/contact.txt
23
24The Tests Themselves
25====================
26
27The ``app`` object is a wrapper around your application, with many
28methods to make testing convenient.  Here's an example test script::
29
30    def test_myapp():
31        res = app.get('/view', params={'id': 10})
32        # We just got /view?id=10
33        res.mustcontain('Item 10')
34        res = app.post('/view', params={'id': 10, 'name': 'New item
35            name'})
36        # The app does POST-and-redirect...
37        res = res.follow()
38        assert res.request.url == '/view?id=10'
39        res.mustcontain('New item name')
40        res.mustcontain('Item updated')
41
42The methods of the ``app`` object (a ``paste.tests.fixture.TestApp``
43object):
44
45``get(url, params={}, headers={}, status=None)``:
46    Gets the URL.  URLs are based in the root of your application; no
47    domains are allowed.  Parameters can be given as a dictionary, or
48    included directly in the ``url``.  Headers can also be added.
49
50    This tests that the status is a ``200 OK`` or a redirect header,
51    unless you pass in a ``status``.  A status of ``"*"`` will never
52    fail; or you can assert a specific status (like ``500``).
53
54    Also, if any errors are written to the error stream this will
55    raise an error.
56
57``post(url, params={}, headers={}, status=None, upload_files=())``:
58    POSTS to the URL.  Like GET, except also allows for uploading
59    files.  The uploaded files are a list of ``(field_name, filename,
60    file_content)``.
61
62    If you don't want to do a urlencoded post body, you can put a
63    ``content-type`` header in your header, and pass the body in as a
64    string with ``params``.
65
66The response object:
67
68``header(header_name, [default])``:
69    Returns the named header.  It's an error if there is more than one
70    matching header.  If you don't provide a default, it is an error
71    if there is no matching header.
72
73``all_headers(header_name):``
74    Returns a list of all matching headers.
75
76``follow(**kw)``:
77    Follows the redirect, returning the new response.  It is an error
78    if this response wasn't a redirect.  Any keyword arguments are
79    passed to ``app.get`` (e.g., ``status``).
80
81``x in res``:
82    Returns True if the string is found in the response.  Whitespace
83    is normalized for this test.
84
85``mustcontain(*strings)``:
86    Raises an error if any of the strings are not found in the
87    response.
88
89``showbrowser()``:
90    Opens the HTML response in a browser; useful for debugging.
91
92``str(res)``:
93    Gives a slightly-compacted version of the response.
94
95``click(description=None, linkid=None, href=None, anchor=None, index=None, verbose=False)``:
96    Clicks the described link (`see docstring for more
97    <./class-paste.fixture.TestResponse.html#click>`_)
98
99``forms``:
100    Return a dictionary of forms; you can use both indexes (refer to
101    the forms in order) or the string ids of forms (if you've given
102    them ids) to identify the form.  See `Form Submissions <#form-submissions>`_ for
103    more on the form objects.
104
105Request objects:
106
107``url``:
108    The url requested.
109
110``environ``:
111    The environment used for the request.
112
113``full_url``:
114    The url with query string.
115
116Form Submissions
117================
118
119You can fill out and submit forms from your tests.  First you get the
120form::
121
122    res = testapp.get('/entry_form')
123    form = res.forms[0]
124
125Then you fill it in fields::
126
127    # when there's one unambiguous name field:
128    form['name'] = 'Bob'
129    # Enter something into the first field named 'age'
130    form.set('age', '45', index=1)
131
132Finally you submit::
133
134    # Submit with no particular submit button pressed:
135    form.submit()
136    # Or submit a button:
137    form.submit('submit_button_name')
138
139Framework Hooks
140===============
141
142Frameworks can detect that they are in a testing environment by the
143presence (and truth) of the WSGI environmental variable
144``"paste.testing"``.
145
146More generally, frameworks can detect that something (possibly a test
147fixture) is ready to catch unexpected errors by the presence and truth
148of ``"paste.throw_errors"`` (this is sometimes set outside of testing
149fixtures too, when an error-handling middleware is in place).
150
151Frameworks that want to expose the inner structure of the request may
152use ``"paste.testing_variables"``.  This will be a dictionary -- any
153values put into that dictionary will become attributes of the response
154object.  So if you do ``env["paste.testing_variables"]['template'] =
155template_name`` in your framework, then ``response.template`` will be
156``template_name``.
157