TestApp ======= .. >>> import json >>> import sys >>> from webtest.app import TestApp >>> from webob import Request >>> from webob import Response >>> def application(environ, start_response): ... req = Request(environ) ... if req.path_info.endswith('.html'): ... content_type = 'text/html' ... body = '
'.encode('latin-1') ... elif req.path_info.endswith('.xml'): ... content_type = 'text/xml' ... body = 'hey!'.encode('latin-1') ... elif req.path_info.endswith('.json'): ... content_type = 'application/json' ... body = json.dumps({"a": 1, "b": 2}).encode('latin-1') ... elif '/resource/' in req.path_info: ... content_type = 'application/json' ... body = json.dumps(dict(id=1, value='value')).encode('latin-1') ... resp = Response(body, content_type=content_type) ... return resp(environ, start_response) >>> app = TestApp(application) Making Requests --------------- To make a request, use: .. code-block:: python app.get('/path', [params], [headers], [extra_environ], ...) This call to :meth:`~webtest.app.TestApp.get` does a request for ``/path``, with any params, extra headers or WSGI environment keys that you indicate. This returns a :class:`~webtest.response.TestResponse` object, based on :class:`webob.response.Response`. It has some additional methods to make it easier to test. If you want to do a POST request, use: .. code-block:: python app.post('/path', {'vars': 'values'}, [headers], [extra_environ], [upload_files], ...) Specifically the second argument of :meth:`~webtest.app.TestApp.post` is the *body* of the request. You can pass in a dictionary (or dictionary-like object), or a string body (dictionary objects are turned into HTML form submissions). You can also pass in the keyword argument upload_files, which is a list of ``[(fieldname, filename, field_content)]``. File uploads use a different form submission data type to pass the structured data. You can use :meth:`~webtest.app.TestApp.put` and :meth:`~webtest.app.TestApp.delete` for PUT and DELETE requests. Making JSON Requests -------------------- Webtest provide some facilities to test json apis. The ``*_json`` methods will transform data to json before ``POST``/``PUT`` and add the correct ``Content-Type`` for you. Also Response have an attribute :attr:`~webtest.response.TestResponse.json` to allow you to retrieve json contents as a python dict. Doing *POST* request with :meth:`webtest.app.TestApp.post_json`: .. code-block:: python >>> resp = app.post_json('/resource/', dict(id=1, value='value')) >>> print(resp.request) POST /resource/ HTTP/1.0 Content-Length: 27 Content-Type: application/json ... >>> resp.json == {'id': 1, 'value': 'value'} True Doing *GET* request with :meth:`webtest.app.TestApp.get` and using :attr:`~webtest.response.TestResponse.json`: To just parse body of the response, use Response.json: .. code-block:: python >>> resp = app.get('/resource/1/') >>> print(resp.request) GET /resource/1/ HTTP/1.0 ... >>> resp.json == {'id': 1, 'value': 'value'} True Modifying the Environment & Simulating Authentication ------------------------------------------------------ The best way to simulate authentication is if your application looks in ``environ['REMOTE_USER']`` to see if someone is authenticated. Then you can simply set that value, like: .. code-block:: python app.get('/secret', extra_environ=dict(REMOTE_USER='bob')) If you want *all* your requests to have this key, do: .. code-block:: python app = TestApp(my_app, extra_environ=dict(REMOTE_USER='bob')) If you have to use HTTP authorization you can use the ``.authorization`` property to set the ``HTTP_AUTHORIZATION`` key of the extra_environ dictionary: .. code-block:: python app = TestApp(my_app) app.authorization = ('Basic', ('user', 'password')) You can also use bearer token or JWT authorization types: .. code-block:: python app = TestApp(my_app) app.authorization = ('Bearer', 'mytoken') # or app.authorization = ('JWT', 'myjwt') Testing a non wsgi application ------------------------------ You can use WebTest to test an application on a real web server. Just pass an url to the `TestApp` instead of a WSGI application:: app = TestApp('http://my.cool.websi.te') You can also use the ``WEBTEST_TARGET_URL`` env var to switch from a WSGI application to a real server without having to modify your code:: os.environ['WEBTEST_TARGET_URL'] = 'http://my.cool.websi.te' app = TestApp(wsgiapp) # will use the WEBTEST_TARGET_URL instead of the wsgiapp By default the proxy will use ``httplib`` but you can use other backends by adding an anchor to your url:: app = TestApp('http://my.cool.websi.te#urllib3') app = TestApp('http://my.cool.websi.te#requests') app = TestApp('http://my.cool.websi.te#restkit') What Is Tested By Default -------------------------- A key concept behind WebTest is that there's lots of things you shouldn't have to check everytime you do a request. It is assumed that the response will either be a 2xx or 3xx response; if it isn't an exception will be raised (you can override this for a request, of course). The WSGI application is tested for WSGI compliance with a slightly modified version of :mod:`python:wsgiref.validate` (modified to support arguments to ``InputWrapper.readline``) automatically. Also it checks that nothing is printed to the ``environ['wsgi.errors']`` error stream, which typically indicates a problem (one that would be non-fatal in a production situation, but if you are testing is something you should avoid). To indicate another status is expected, use the keyword argument ``status=404`` to (for example) check that it is a 404 status, or ``status="*"`` to allow any status, or ``status="400 Custom Bad Request"`` to use custom reason phrase. If you expect errors to be printed, use ``expect_errors=True``.