Tuesday, August 30, 2011

Testing authenticated handlers in Tornado

Tornado is an excellent little async HTTP framework and I have been using it in many projects I've been part of. Documentation is a bit lacking but access to sources makes all the difference.

But say you are implementing handlers that require authentication. You are versed in TDD and you want your handlers tested as well. Here's a little trick that I use to "mock" the authenticated user.

When writing tests, I use excellent pymox library and the following example uses that, but you can use any mocking library you prefer.

Here is the Handler which we want to test:
# encoding: utf-8
__author__ = 'alex'
import tornado.web

class Protected(tornado.web.RequestHandler):
    def get_current_user(self):
        # get an user from somewhere
 return self.retrieve_user_from_db()

    def get(self):
We want to test the get() method, but since it's decorated with authenticated decorator, testing the method will result in response code 403. To overcome that (and make our test green!), we'll mock get_current_user() in our Test - it's that easy:
# encoding: utf-8
__author__ = 'alex'
import os, os.path, sys
import tornado.web
import tornado.testing
import mox

import project.handlers

class TestAuthenticatedHandlers(tornado.testing.AsyncHTTPTestCase):
    def get_app(self):
        app = tornado.web.Application([(r'/protected', project.handlers.Protected)])
        self.mox.StubOutWithMock(tornado.web.RequestHandler, 'get_current_user', use_mock_anything=True)
        return app

    def setUp(self):
        self.mox = mox.Mox()
        super(TestAuthenticatedHandlers, self).setUp()

    def tearDown(self):

    def test_new_admin(self):
        resp = self.fetch('/protected')
        self.assertEqual(resp.code, 200)
I stubbed out the get_current_code() method on the RequestHandler, but if you have implemented a BaseHandler and extend your handlers from that, you should mock its method (it's closer to your code).

I mock objects extensively in my tests - it is a great practise and helps you focus better on the actual code you're testing. Hopefully this little trick will help you as well.