Making Requests

To make a request, use:

app.get('/path', [params], [headers], [extra_environ], ...)

This call to get() does a request for /path, with any params, extra headers or WSGI environment keys that you indicate. This returns a TestResponse object, based on webob.response.Response. It has some additional methods to make it easier to test.

If you want to do a POST request, use:

app.post('/path', {'vars': 'values'}, [headers], [extra_environ],
         [upload_files], ...)

Specifically the second argument of 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 put() and 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 json to allow you to retrieve json contents as a python dict.

Doing POST request with webtest.app.TestApp.post_json():

>>> 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'}

Doing GET request with webtest.app.TestApp.get() and using json:

To just parse body of the response, use Response.json:

>>> resp = app.get('/resource/1/')
>>> print(resp.request)
GET /resource/1/ HTTP/1.0

>>> resp.json == {'id': 1, 'value': 'value'}

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:

app.get('/secret', extra_environ=dict(REMOTE_USER='bob'))

If you want all your requests to have this key, do:

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:

app = TestApp(my_app)
app.authorization = ('Basic', ('user', 'password'))

You can also use bearer token or JWT authorization types:

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