![]() |
Fortunately, I always test my code.
|
For functional tests, I use PasteDeploy to load my Pyramid application and webtest to issue requests and test responses.
from unittest import TestCase
from webtest import TestApp
from os.path import abspath
from paste.deploy import loadapp
# load the WSGI stack
conf_dir = abspath(__file__ + '/../..')
app = loadapp('config:test.ini', relative_to=conf_dir)
app = TestApp(app)
class TestHome(TestCase):
def test_home(self):
response = app.get('/', status=200)
# do assertions ...
webtest.TestApp wraps your WSGI stack to provide convenient handles such as .get() .post() .put() .delete() ... It saves you from making raw WSGI calls like this: app(environ, start_response)
These tests are just plain Python function calls. So how can I run these function calls against my production server? WSGIProxy!
WSGIProxy
Bleh. Proxy. I have never setup a proxy server, don't like proxies, I don't understand them. I'm gonna stop reading now.
First of all, WSGIProxy is NOT about setting up a proxy server. It's a WSGI app which turns your WSGI calls into HTTP requests. Pretty cool. This is how I set it up:
from wsgiproxy.app import WSGIProxyApp
from webtest import TestApp
remote_host = "http://127.0.0.1.6543"
app = WSGIProxyApp(remote_host)
app = TestApp(app, extra_environ={"REMOTE_ADDR": remote_host})
class TestHome(TestCase):
def test_home(self):
response = app.get('/', status=200)
# do assertions ...
Whenever I call
app.get('/', status=200) an actual HTTP request is issued to the server. Neat! Now I can run all my functional tests against my server in production with minor code change!But what if I don't want to change my code back and forth? nose-testconfig!
nose-testconfig
Whether I want to run my functional tests against my WSGI stack or a remote server: I don't want to touch my code!
I want to pass the remote URL on the command line, or fallback on my WSGI stack if no URL was given. But have you tried passing your own arguments to nose?
$ nosetests --url http://example.com Usage: nosetests [options] nosetests: error: no such option: --url
You can't.
The library nose-testconfig was specifically written to address this concern. Once installed, you can do:
nosetests --tc=url:http://example.com
This will be mapped into a key/value dictionary in your Python code:
from testconfig import config assert config["url"] == "http://example.com" # True
Now I can write:
def get_app():
from testconfig import config
remote_host = config.get('url')
# Extra environ headers to be passed to TestApp
extra_environ = {}
if remote_host:
# Setup a WSGI proxy
from wsgiproxy.app import WSGIProxyApp
extra_environ['REMOTE_ADDR'] = remote_host
app = WSGIProxyApp(remote_host)
else:
# Load the local WSGI stack
from os.path import abspath
from paste.deploy import loadapp
conf_dir = abspath(__file__ + '/../..')
app = loadapp('config:test.ini', relative_to=conf_dir)
from webtest import TestApp
app = TestApp(app, extra_environ=extra_environ)
return app
app = get_app()
class TestHome(TestCase):
def test_home(self):
response = app.get('/', status=200)
# do assertions ...
Now my test infrastructure allows me to test locally as I develop or test remotely once code is deployed, just by passing a URL on the command line.
Final note
Of course, coverage will look broken with WSGIProxy. Moreover, keep in mind that mocking / patching your code might not behave as you would normally expect. If you only want to run a subset of your tests, you may want to use nose's attribute selector plugin to select them.

Thanks for this really useful technique. In case it saves someone else the google, you also have to
ReplyDeletefrom paste.deploy import loadapp
Thanks Joshua! Fixed.
ReplyDelete