1 Testing Applications with Paste 2 +++++++++++++++++++++++++++++++ 3 4 :author: Ian Bicking <ianb (a] colorstudy.com> 5 :revision: $Rev$ 6 :date: $LastChangedDate$ 7 8 .. contents:: 9 10 Introduction 11 ============ 12 13 Paste includes functionality for testing your application in a 14 convenient manner. These facilities are quite young, and feedback is 15 invited. Feedback and discussion should take place on the 16 `Paste-users list 17 <http://groups.google.com/group/paste-users>`_. 18 19 These facilities let you test your Paste and WSGI-based applications 20 easily and without a server. 21 22 .. include:: include/contact.txt 23 24 The Tests Themselves 25 ==================== 26 27 The ``app`` object is a wrapper around your application, with many 28 methods 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 42 The methods of the ``app`` object (a ``paste.tests.fixture.TestApp`` 43 object): 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 66 The 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 105 Request 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 116 Form Submissions 117 ================ 118 119 You can fill out and submit forms from your tests. First you get the 120 form:: 121 122 res = testapp.get('/entry_form') 123 form = res.forms[0] 124 125 Then 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 132 Finally 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 139 Framework Hooks 140 =============== 141 142 Frameworks can detect that they are in a testing environment by the 143 presence (and truth) of the WSGI environmental variable 144 ``"paste.testing"``. 145 146 More generally, frameworks can detect that something (possibly a test 147 fixture) is ready to catch unexpected errors by the presence and truth 148 of ``"paste.throw_errors"`` (this is sometimes set outside of testing 149 fixtures too, when an error-handling middleware is in place). 150 151 Frameworks that want to expose the inner structure of the request may 152 use ``"paste.testing_variables"``. This will be a dictionary -- any 153 values put into that dictionary will become attributes of the response 154 object. So if you do ``env["paste.testing_variables"]['template'] = 155 template_name`` in your framework, then ``response.template`` will be 156 ``template_name``. 157