The past week I have had the pleasure to write a small web based system in python. Being a devout TDD practitioner the first step in learning python was of cause to understand the unittest module. Next thing was to see what mocking frameworks are available. To my delight I found that mockito is available for python as well (here).
The application I’m working on is a web service API to access our application using Cloud to device messaging (c2dm) from Google. To provide the web API I am using the Tornado framework.
As web frameworks goes they are usually tricky to test drive. Not so with Tornado. It ships with it’s own testing framework that allows the developer to set up a round trip request/response cycle within a unit test using the tornado.testing module. Given clear module boundaries and mockito to stub out dependencies it’s an easy thing to test drive Tornado web application development.
The tornado.testing module comes with a couple of very useful classes. I used AsyncHTTPTestCase to test the RequestHandler classes. To make sure that only failing tests writes to the log I also inherited LogTrapTestCase.
Below is some sample code written to allow device registration with our application server. First is the test in it’s entirety and then the implementation.
The tests are in the module c2dm_registration_service_test.py:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
import unittest
from mockito import mock, verify
from tornado.testing import AsyncHTTPTestCase, LogTrapTestCase
from c2dm_registration_service import C2DMRegistrationService
class C2DMRegistrationServiceTest(AsyncHTTPTestCase, LogTrapTestCase):
_deviceid = 'deviceid'
_registrationid = 'registrationid'
_http_success_code = 200
_successfull_new_registration = '/register_device?deviceid=' + _deviceid + '®istrationid=' + _registrationid
_successfull_update_registration = '/update_device?deviceid=' + _deviceid + '®istrationid=' + _registrationid
def setUp(self):
self._registration_handler = mock()
AsyncHTTPTestCase.setUp(self)
def get_app(self):
return C2DMRegistrationService(self._registration_handler)
def test_register_new_device(self):
self.http_client.fetch(self.get_url(self._successfull_new_registration),
self.stop)
response = self.wait()
self.assertEqual(self._http_success_code, response.code)
verify(self._registration_handler).handle_registration(self._deviceid, self._registrationid)
def test_update_registration_for_registered_device(self):
self.http_client.fetch(self.get_url(self._successfull_update_registration),
self.stop)
response = self.wait()
self.assertEquals(self._http_success_code, response.code)
verify(self._registration_handler).handle_registration_id_change_for_device(self._deviceid, self._registrationid)
if __name__ == '__main__':
unittest.main() |
The production code is in the module c2dm_registration_service.py:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
import tornado.web
from c2dm import RegistrationHandler
class C2DMRegistrationService(tornado.web.Application):
def __init__(self, registration_handler = RegistrationHandler()):
handlers = [
(r'/register_device', RegisterNewDeviceHandler),
(r'/update_device', UpdateDeviceRegistrationHandler) ]
tornado.web.Application.__init__(self, handlers)
self.registration_handler = registration_handler
class BaseHandler(tornado.web.RequestHandler):
@property
def registration_handler(self):
return self.application.registration_handler
class RegisterNewDeviceHandler(BaseHandler):
def get(self):
self.registration_handler.handle_registration(self.get_argument('deviceid'), self.get_argument('registrationid'))
class UpdateDeviceRegistrationHandler(BaseHandler):
def get(self):
self.registration_handler.handle_registration_id_change_for_device(self.get_argument('deviceid'), self.get_argument('registrationid')) |
Added to this one more class in the mondule c2dm.py. So far it is just a stub:
|
1 2 3 4 |
class RegistrationHandler():
def dummy(self):
print("hello") |
With this knowledge I can’t find any excuses not to test drive web development in Python. It is such an easy task and takes no time at all. It is well worth the effort even when creating pilots, which most likely will end up becoming production code in the not to distant future.
