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 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
|
Testing Applications with Paste
+++++++++++++++++++++++++++++++
:author: Ian Bicking <ianb@colorstudy.com>
:revision: $Rev$
:date: $LastChangedDate$
.. contents::
Introduction
============
Paste includes functionality for testing your application in a
convenient manner. These facilities are quite young, and feedback is
invited. Feedback and discussion should take place on the
`Paste-users list
<http://groups.google.com/group/paste-users>`_.
These facilities let you test your Paste and WSGI-based applications
easily and without a server.
.. include:: include/contact.txt
The Tests Themselves
====================
The ``app`` object is a wrapper around your application, with many
methods to make testing convenient. Here's an example test script::
def test_myapp():
res = app.get('/view', params={'id': 10})
# We just got /view?id=10
res.mustcontain('Item 10')
res = app.post('/view', params={'id': 10, 'name': 'New item
name'})
# The app does POST-and-redirect...
res = res.follow()
assert res.request.url == '/view?id=10'
res.mustcontain('New item name')
res.mustcontain('Item updated')
The methods of the ``app`` object (a ``paste.tests.fixture.TestApp``
object):
``get(url, params={}, headers={}, status=None)``:
Gets the URL. URLs are based in the root of your application; no
domains are allowed. Parameters can be given as a dictionary, or
included directly in the ``url``. Headers can also be added.
This tests that the status is a ``200 OK`` or a redirect header,
unless you pass in a ``status``. A status of ``"*"`` will never
fail; or you can assert a specific status (like ``500``).
Also, if any errors are written to the error stream this will
raise an error.
``post(url, params={}, headers={}, status=None, upload_files=())``:
POSTS to the URL. Like GET, except also allows for uploading
files. The uploaded files are a list of ``(field_name, filename,
file_content)``.
If you don't want to do a urlencoded post body, you can put a
``content-type`` header in your header, and pass the body in as a
string with ``params``.
The response object:
``header(header_name, [default])``:
Returns the named header. It's an error if there is more than one
matching header. If you don't provide a default, it is an error
if there is no matching header.
``all_headers(header_name):``
Returns a list of all matching headers.
``follow(**kw)``:
Follows the redirect, returning the new response. It is an error
if this response wasn't a redirect. Any keyword arguments are
passed to ``app.get`` (e.g., ``status``).
``x in res``:
Returns True if the string is found in the response. Whitespace
is normalized for this test.
``mustcontain(*strings)``:
Raises an error if any of the strings are not found in the
response.
``showbrowser()``:
Opens the HTML response in a browser; useful for debugging.
``str(res)``:
Gives a slightly-compacted version of the response.
``click(description=None, linkid=None, href=None, anchor=None, index=None, verbose=False)``:
Clicks the described link (`see docstring for more
<./class-paste.fixture.TestResponse.html#click>`_)
``forms``:
Return a dictionary of forms; you can use both indexes (refer to
the forms in order) or the string ids of forms (if you've given
them ids) to identify the form. See `Form Submissions <#form-submissions>`_ for
more on the form objects.
Request objects:
``url``:
The url requested.
``environ``:
The environment used for the request.
``full_url``:
The url with query string.
Form Submissions
================
You can fill out and submit forms from your tests. First you get the
form::
res = testapp.get('/entry_form')
form = res.forms[0]
Then you fill it in fields::
# when there's one unambiguous name field:
form['name'] = 'Bob'
# Enter something into the first field named 'age'
form.set('age', '45', index=1)
Finally you submit::
# Submit with no particular submit button pressed:
form.submit()
# Or submit a button:
form.submit('submit_button_name')
Framework Hooks
===============
Frameworks can detect that they are in a testing environment by the
presence (and truth) of the WSGI environmental variable
``"paste.testing"``.
More generally, frameworks can detect that something (possibly a test
fixture) is ready to catch unexpected errors by the presence and truth
of ``"paste.throw_errors"`` (this is sometimes set outside of testing
fixtures too, when an error-handling middleware is in place).
Frameworks that want to expose the inner structure of the request may
use ``"paste.testing_variables"``. This will be a dictionary -- any
values put into that dictionary will become attributes of the response
object. So if you do ``env["paste.testing_variables"]['template'] =
template_name`` in your framework, then ``response.template`` will be
``template_name``.
|