| 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``. |