WebTest
repository·main·Indexed 43 days ago
https://github.com/pylons/webtestA testing library for WSGI applications that enables full-stack functional, integration, and acceptance testing by simulating HTTP requests without requiring a running HTTP server. It provides the TestApp class for simulating requests, TestResponse for inspecting results, and specialized tools for interacting with HTML forms, JSON APIs, and file uploads. Version 3.0.8.dev0.
What's inside WebTest
- WebTest is a library designed to wrap any WSGI application, allowing you to send test requests directly to the application without the need to start a live HTTP server. It provides a convenient way to perform full-stack testing for applications built with any WSGI-compatible framework.
What is WebTest and how does it work?
mainWebTest is a library for functional, integration, or acceptance testing of WSGI-based web applications.
Instead of starting a live HTTP server, WebTest wraps your WSGI application and simulates how a WSGI HTTP server would call it. This allows you to test the full stack of your application (including template logic and web routing) without the overhead of a real network server. It is compatible with almost any Python web framework that supports the WSGI interface.
Detect testing environments using WSGI environment variables
mainWeb frameworks can identify that they are running within a WebTest environment by checking for the presence and truthiness of the WSGI environment variablepaste.testing. This variable is inherited frompaste.fixtureand is used to signal that the application is being tested.Expose request internals via paste.testing_variables
mainFrameworks can expose internal request data (like template names or metadata) to the WebTest response object by using the
paste.testing_variablesWSGI environment variable.Any key-value pairs added to the dictionary assigned to
paste.testing_variableswill be attached as attributes to the resultingresponseobject. For example, settingenv['paste.testing_variables']['template'] = 'index.html'allows you to access the value viaresponse.templatein your test assertions.# Inside the framework/app logic: env['paste.testing_variables']['template'] = 'index.html' # Inside the WebTest test: assert response.template == 'index.html'Understand the TestResponse object
mainThe
TestResponseobject is the primary way to inspect the results of a request made viaTestApp. It is based onwebob.response.Responsebut includes additional methods and attributes specifically designed for testing, such as content assertion, link clicking, and automated redirect following.from webtest.app import TestApp from webob import Request, Response def application(environ, start_response): return Response('Hello World') app = TestApp(application) res = app.get('/') # res is a TestResponse objectHandle unexpected errors in testing using paste.throw_errors
mainTo signal that a test fixture or environment is ready to catch unexpected errors, frameworks can check for the presence and truthiness of thepaste.throw_errorsWSGI environment variable. This is useful when error-handling middleware is active and you want to ensure errors are propagated to the test runner.Install WebTest
mainYou can install the latest stable release of WebTest using
piporeasy_install. If you require the development version, you can install directly from the source tarball.$ pip install WebTest $ easy_install WebTest # For the development version: $ pip install https://nodeload.github.com/Pylons/webtest/tar.gz/mainSubmit a form
mainOnce the form is filled, use the
.submit()method to send the request. You can submit the form generally, or target a specific submit button.- General Submit:
form.submit() - By Name:
form.submit('submit_button_name') - By Index:
form.submit('submit_button_name', index=1) - By Value:
form.submit('submit_button_name', value='Submit 2')
# Submit without a specific button res = form.submit() # Submit a specific button by name res = form.submit('submit') # Submit a specific button by name and index res = form.submit('submit', index=1) # Submit a specific button by name and value res = form.submit('submit', value="Submit 2")- General Submit:
Quick start with TestApp
mainThe core of WebTest is the
TestAppclass, which acts as a wrapper for your WSGI application. You instantiateTestAppwith your application object, and then use it to perform HTTP requests likeget(),post(), etc. The resulting response object allows you to inspect status codes, headers, and body content.from webtest import TestApp # 1. Define or import your WSGI application def application(environ, start_response): headers = [('Content-Type', 'text/html; charset=utf8')] start_response('200 OK', headers) return [b'<html><body>form</body></html>'] # 2. Wrap the application in TestApp app = TestApp(application) # 3. Perform an HTTP GET request resp = app.get('/') # 4. Verify the results assert resp.status == '200 OK' assert resp.status_int == 200 assert resp.content_type == 'text/html' resp.mustcontain('<html>')Upload files in a form
mainTo simulate file uploads, use the
webtest.Uploadclass. You can provide the filename, the raw bytes, and the content type.- For a single file:
form['file'] = Upload('path/to/file'). - For multiple files (where the input has a
multipleattribute): Pass a list ofUploadobjects.
from webtest import Upload # Single file form['file'] = Upload('README.rst') form['file'] = Upload('README.rst', b'data', 'text/x-rst') # Multiple files form['files'] = [ Upload('README.rst'), Upload('LICENSE.rst'), ]- For a single file:
Handle select and multiple select fields
mainSelect fields can be manipulated by value or by the visible text of the options.
- Simple Select: Assign the value directly:
form['select'] = 'option1'. - Multiple Select: Assign a list of values:
form['multiple'] = ['option1', 'option2']. - By Text: Use
.select(text="...")for single selects or.select_multiple(texts=["..."])for multiple selects. - Bypassing Validation: If you need to set a value that is not present in the
<option>tags, use.force_value().
# Simple select form['select'] = 'option1' form['select'].select(text="Option 2") # Multiple select form['multiple'] = ['option1'] form['multiple'].select_multiple(texts=["Option 1", "Option 2"]) # Force invalid values form['select'].force_value(['optionX'])- Simple Select: Assign the value directly:
Simulate authentication and modify the environment
mainYou can simulate authenticated users or specific environment states using
extra_environor the.authorizationproperty.Simulating REMOTE_USER
If your app checks
environ['REMOTE_USER'], you can set it per request or globally:- Per request:
app.get('/secret', extra_environ=dict(REMOTE_USER='bob')) - Globally:
app = TestApp(my_app, extra_environ=dict(REMOTE_USER='bob'))
Using HTTP Authorization
Set the
.authorizationproperty on theTestAppinstance to set theHTTP_AUTHORIZATIONheader. Supported formats include:- Basic:
app.authorization = ('Basic', ('user', 'password')) - Bearer:
app.authorization = ('Bearer', 'mytoken') - JWT:
app.authorization = ('JWT', 'myjwt')
app.get('/secret', extra_environ=dict(REMOTE_USER='bob')) app = TestApp(my_app) app.authorization = ('Basic', ('user', 'password')) app.authorization = ('Bearer', 'mytoken')- Per request: