Search code examples
pythontestingwsgifunctional-testing

Best Way to Run Functional Tests of a WSGI Application?


I'm writing a pair of simple WSGI applications to get a feel for the standard, and I'm to the point where I'd like to test that my applications are working as expected. Now I'm trying to figure out the best way start and stop a server hosting those applications.

My first thought was to start up the SimpleServer from wsgiref in the setUp method and shut it down in the tearDown method. Since serve_forever blocks, I did that work in a separate Thread. Unfortunately, if I use serve_forever to start the server, the secondary thread it's in never dies, and it turns out threading doesn't expose a way to kill a Thread.

Now I'm considering using something like this as my run implementation in the server's Thread:

while keep_going:
    httpd.handle_request()

where keep_going is a variable that I set to True in my setUp and set to False in my tearDown.

Is there a better way? I was hoping to write these apps and tests using just the standard library—the better to understand what I'm abstracting away when I use a lib/framework—but this seems like an awful lot of trouble.

Thanks.


Solution

  • The best way to test WSGI applications is WebTest, which eliminates the need to spawn test servers for your app. Instead, it allows you to do make HTTP requests to your WSGI app as method calls on a TestApp object:

    >>> from webtest import TestApp
    >>> from myapp import my_wsgi_app
    >>> app = TestApp(my_wsgi_app)
    >>> res = app.get('/')
    >>> res.status
    '200 OK'
    >>> res.status_int
    200
    >>> params = {'email': '[email protected]', 'password': 'foo', }
    >>> res = app.post('/login/', params)
    >>> res.status_int
    302
    

    I usually have a base test class that will add a self.app attribute to my test cases that points to a TestApp instance for the WSGI app I'm testing. So my test methods end up looking something like:

    def test_index_page(self):
        res = self.app.get('/')
        self.failUnlessEqual(res.status_int, 200)