Testing a WSGI application normally involves starting a server at a local host and port, then pointing your test code to that address. Instead, this library lets you intercept calls to any specific host/port combination and redirect them into a WSGI application importable by your test program. Thus, you can avoid spawning multiple processes or threads to test your Web app.
How Does It Work?
wsgi_intercept works by replacing
httplib.HTTPConnection with a subclass,
wsgi_intercept.WSGI_HTTPConnection. This class then redirects specific server/port combinations into a WSGI application by emulating a socket. If no intercept is registered for the host and port requested, those requests are passed on to the standard handler.
add_wsgi_intercept(host, port, app_create_fn, script_name='') and
remove_wsgi_intercept(host,port) specify which URLs should be redirect into what applications. Note especially that
app_create_fn is a function object returning a WSGI application;
SCRIPT_NAME in the WSGI app's environment, if set.
easy_install command is bundled with the setuptools module)
To use a development version of wsgi_intercept, run:
Unfortunately each of the Web testing frameworks uses its own specific mechanism for making HTTP call-outs, so individual implementations are needed. Below are the packages supported and how to create an intercept.
urllib2 is a standard Python module, and
urllib2.urlopen is a pretty
normal way to open URLs.
The following code will install the WSGI intercept stuff as a default urllib2 handler:
>>> from wsgi_intercept.urllib2_intercept import install_opener >>> install_opener() #doctest: +ELLIPSIS <urllib2.OpenerDirector instance at ...> >>> import wsgi_intercept >>> from wsgi_intercept.test_wsgi_app import create_fn >>> wsgi_intercept.add_wsgi_intercept('some_host', 80, create_fn) >>> import urllib2 >>> urllib2.urlopen('http://some_host:80/').read() 'WSGI intercept successful!\\n'
The only tricky bit in there is that different handler classes need to be constructed for Python 2.3 and Python 2.4, because the httplib interface changed between those versions.
httplib2 is a 3rd party extension of the built-in
httplib. To intercept
requests, it is similar to urllib2:
>>> from wsgi_intercept.httplib2_intercept import install >>> install() >>> import wsgi_intercept >>> from wsgi_intercept.test_wsgi_app import create_fn >>> wsgi_intercept.add_wsgi_intercept('some_host', 80, create_fn) >>> import httplib2 >>> resp, content = httplib2.Http().request('http://some_host:80/', 'GET') >>> content 'WSGI intercept successful!\\n'
(Contributed by David "Whit" Morris.)
webtest is an extension to
unittest that has some nice functions for
testing Web sites.
To install the WSGI intercept handler, do
>>> import wsgi_intercept.webtest_intercept >>> class WSGI_Test(wsgi_intercept.webtest_intercept.WebCase): ... HTTP_CONN = wsgi_intercept.WSGI_HTTPConnection ... HOST='localhost' ... PORT=80 ... ... def setUp(self): ... wsgi_intercept.add_wsgi_intercept(self.HOST, self.PORT, create_fn) ... >>>
webunit needed to be patched to support different scheme handlers. The patched package is in webunit/wsgi_webunit/, and the only file that was changed was webunittest.py; the original is in webunittest-orig.py.
To install the WSGI intercept handler, do
>>> from httplib import HTTP >>> import wsgi_intercept.webunit_intercept >>> class WSGI_HTTP(HTTP): ... _connection_class = wsgi_intercept.WSGI_HTTPConnection ... >>> class WSGI_WebTestCase(wsgi_intercept.webunit_intercept.WebTestCase): ... scheme_handlers = dict(http=WSGI_HTTP) ... ... def setUp(self): ... wsgi_intercept.add_wsgi_intercept('127.0.0.1', 80, create_fn) ... >>>
>>> import wsgi_intercept.mechanize_intercept >>> from wsgi_intercept.test_wsgi_app import create_fn >>> wsgi_intercept.add_wsgi_intercept('some_host', 80, create_fn) >>> b = wsgi_intercept.mechanize_intercept.Browser() >>> response = b.open('http://some_host:80') >>> response.read() 'WSGI intercept successful!\\n'
zope.testbrowser is also pretty easy
>>> import wsgi_intercept.zope_testbrowser >>> from wsgi_intercept.test_wsgi_app import create_fn >>> wsgi_intercept.add_wsgi_intercept('some_host', 80, create_fn) >>> b = wsgi_intercept.zope_testbrowser.WSGI_Browser('http://some_host:80/') >>> b.contents 'WSGI intercept successful!\\n'
Pursuant to Ian Bicking's "best Web testing framework" post, Titus Brown put together an in-process HTTP-to-WSGI interception mechanism for his own Web testing system, twill. Because the mechanism is pretty generic -- it works at the httplib level -- Titus decided to try adding it into all of the other Python Web testing frameworks.
This is the result.
Mocking your HTTP Server
Marc Hedlund has gone one further, and written a full-blown mock HTTP
server for wsgi_intercept. Combined with wsgi_intercept itself, this
lets you entirely replace client calls to a server with a mock setup
that hits neither the network nor server code. You can see his work
in the file
mock_http.py to see a test.