Package: glinda
Glinda is a companion library fortornado.  It is an attempt to make your
time with the framework less painful.  In fact, I want to make it downright
enjoyable.  I started down the path of developing HTTP endpoints in Tornado
and needing to test them.  Thetornado.testingpackage is handy for
testing endpoints in isolation.  But what do you do when you have a HTTP
service that is calling other HTTP services asynchronously.  It turns out
that testing that is not as easy as it should be.  That is the first thing
that I tackled and it is the first thing that this library is going to
offer –a way to test non-trivial services.Once you can test your application, the next step is to write a well-behaved
application that fits into the WWW nicely.  Tornado does a pretty nice job
of handling the nitty gritty HTTP details (e.g., CTE, transfer encodings).
It doesn’t provide a clean way to handle representations transparently so
I decided to add that into this library as well.Content HandlingTornado has some internal content decoding accessible by calling theget_body_argumentsmethod oftornado.web.RequestHandler.  It will
decode basic form data,application/x-www-form-urlencodedandmultipart/form-dataspecifically.  Anything else is left up to you.glindaexposes a content handling mix-in that imbues a standardRequestHandlerwith a property that is the decoded request body and
a new method to encode a response.  Here’s what it looks like:classMyHandler(glinda.content.HandlerMixin,web.RequestHandler):defpost(self,*args,**kwargs):body_argument=self.request_body['arg']# do stuffself.send_response(response_dict)self.finish()if__name__=='__main__':glinda.content.register_text_type('application/json',default_charset='utf-8',dumper=json.dumps,loader=json.loads)glinda.content.register_binary_type('application/msgpack',msgpack.dumpb,msgpack.loadb)When the client sends a post with a content type ofapplication/json, it
will decode the binary body to a string according to the HTTP headers and
calljson.loadsto decode the body when you reference therequest_bodyproperty.  Failures are handled by raising aHTTPError(400)so you don’t
have to worry about handling malformed messages.  Thesend_responsemethod will take care of figuring out the appropriate content type based on
any includedAcceptheaders.  All that you have to do is install
encoding and decoding handlers for expected content types.Theglinda.contentpackage implements content handling as described inRFC7231.  Specifically, it decodes request bodies as described insection 3.1and proactive content negotiation as described in sections3.4.1and5.3.TestingHere’s an example of testing a Tornado endpoint that asynchronously calls
another service.  In this case, the application interacts with  with the/addendpoint of some other service.  Testing in isolation can be tricky
without having to have a copy of the service running.  You could mock out
theAsyncHTTPClientand return fake futures and what not but that has
the nasty side-effect of hiding defects around how content type or headers
are handled – no HTTP requests means that you have untested assumptions.The following snippet tests the application under test using theServiceLayerabstraction thatglinda.testingprovides.fromtornadoimporttestingfromglinda.testingimportservicesclassMyServiceTests(testing.AsyncHTTPTestCase):defsetUp(self):service_layer=services.ServiceLayer()self.service=service_layer['adder']# TODO configured your application here using# self.service.url_for('/add') or self.service.hostsuper(MyServiceTests,self).setUp()defget_app(self):returnMyApplication()deftest_that_my_service_calls_other_service(self):self.service.add_response(services.Request('POST','/add'),services.Response(200,body='{"result": 10}'))self.fetch(self.get_url('/do-stuff'),method='GET')recorded=self.service.get_request('/add')self.assertEqual(recorded.method,'POST')self.assertEqual(recorded.body,'[1,2,3,4]')self.assertEqual(recorded.headers['Content-Type'],'application/json')The application under test is linked in by implementing the standardtornado.testing.AsyncHTTPTestCase.get_appmethod.  Then you add in
aglinda.testing.services.ServiceLayerobject and configure it to look
like the services that you depend on by adding endpoints and then configuring
your application to point at the service layer.  When you invoke the
application under test usingself.fetch(...), it will send HTTP requests
through the Tornado stack (using the testingioloop) to the service layer
which will respond appropriately.  The beauty is that the entire HTTP stack is
exercised locally so that you can easily test edge cases such as correct
handling of status codes, custom headers, or malformed bodies without
resorting to deep mocking.Where?Sourcehttps://github.com/dave-shawley/glindaStatushttps://travis-ci.org/dave-shawley/glindaDownloadhttps://pypi.python.org/pypi/glindaDocumentationhttp://glinda.readthedocs.org/en/latestIssueshttps://github.com/dave-shawley/glinda