testing-applications.txt 5.0 KB

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