Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unittesting cherrypy webapp

I recently had to rewrite our rest api, and made the switch from Flask to Cherrypy (mostly due to Python 3 compatibility). But now I'm stuck trying to write my unit tests, Flask has a really nifty built-in test client, that you can use to sent fake requests to your application (without starting a server.) I can't find any similar functionality for Cherrypy, is there such functionality, or am I stuck starting a server and doing actual requests against it?

like image 242
Blubber Avatar asked Jan 10 '13 14:01

Blubber


1 Answers

As far as I know, CherryPy doesn't indeed provide a facility for this type of testing (no running server). But it's fairly easy to do it nonetheless (though it relies on some of the internals of CherryPy).

Here's a simple showcase:

from StringIO import StringIO
import unittest
import urllib

import cherrypy

local = cherrypy.lib.httputil.Host('127.0.0.1', 50000, "")
remote = cherrypy.lib.httputil.Host('127.0.0.1', 50001, "")

class Root(object):
    @cherrypy.expose
    def index(self):
        return "hello world"

    @cherrypy.expose
    def echo(self, msg):
        return msg

def setUpModule():
    cherrypy.config.update({'environment': "test_suite"})

    # prevent the HTTP server from ever starting
    cherrypy.server.unsubscribe()

    cherrypy.tree.mount(Root(), '/')
    cherrypy.engine.start()
setup_module = setUpModule

def tearDownModule():
    cherrypy.engine.exit()
teardown_module = tearDownModule

class BaseCherryPyTestCase(unittest.TestCase):
    def webapp_request(self, path='/', method='GET', **kwargs):
        headers = [('Host', '127.0.0.1')]
        qs = fd = None

        if method in ['POST', 'PUT']:
            qs = urllib.urlencode(kwargs)
            headers.append(('content-type', 'application/x-www-form-urlencoded'))
            headers.append(('content-length', '%d' % len(qs)))
            fd = StringIO(qs)
            qs = None
        elif kwargs:
            qs = urllib.urlencode(kwargs)

        # Get our application and run the request against it
        app = cherrypy.tree.apps['']
        # Let's fake the local and remote addresses
        # Let's also use a non-secure scheme: 'http'
        request, response = app.get_serving(local, remote, 'http', 'HTTP/1.1')
        try:
            response = request.run(method, path, qs, 'HTTP/1.1', headers, fd)
        finally:
            if fd:
                fd.close()
                fd = None

        if response.output_status.startswith('500'):
            print response.body
            raise AssertionError("Unexpected error")

        # collapse the response into a bytestring
        response.collapse_body()
        return response

class TestCherryPyApp(BaseCherryPyTestCase):
    def test_index(self):
        response = self.webapp_request('/')
        self.assertEqual(response.output_status, '200 OK')
        # response body is wrapped into a list internally by CherryPy
        self.assertEqual(response.body, ['hello world'])

    def test_echo(self):
        response = self.webapp_request('/echo', msg="hey there")
        self.assertEqual(response.output_status, '200 OK')
        self.assertEqual(response.body, ["hey there"])

        response = self.webapp_request('/echo', method='POST', msg="hey there")
        self.assertEqual(response.output_status, '200 OK')
        self.assertEqual(response.body, ["hey there"])

if __name__ == '__main__':
    unittest.main()

Edit, I've extended this answer as a CherryPy recipe.

like image 72
Sylvain Hellegouarch Avatar answered Sep 21 '22 12:09

Sylvain Hellegouarch