mardi 27 octobre 2015

How to unittest a CherryPy webapp in Python 3?

I am trying to unittest a webapp in CherryPy. I found this answer which is exactly what I am trying to do, but it is written in Python 2 and I am having some problems getting it to work in Python 3. Here is my modified code:

import unittest
from io import StringIO
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.parse.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.parse.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(b'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, b'200 OK')
        # response body is wrapped into a list internally by CherryPy
        self.assertEqual(response.body, [b'hello world'])

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

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

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

I get an unexpected error from the server for the POST test:

/Library/Frameworks/Python.framework/Versions/3.4/bin/python3.4 /Applications/http://ift.tt/1Bs6Xgx /Users/jweob/PyCharmProjects/coheat-network-control/testUiWebserver.py true
Testing started at 9:01 AM ...
[b'<!DOCTYPE html PUBLIC\n"-//W3C//DTD XHTML 1.0 Transitional//EN"\n"http://ift.tt/kkyg93">\n<html>\n<head>\n    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta>\n    <title>500 Internal Server Error</title>\n    <style type="text/css">\n    #powered_by {\n        margin-top: 20px;\n        border-top: 2px solid black;\n        font-style: italic;\n    }\n\n    #traceback {\n        color: red;\n    }\n    </style>\n</head>\n    <body>\n        <h2>500 Internal Server Error</h2>\n        <p>The server encountered an unexpected condition which prevented it from fulfilling the request.</p>\n        <pre id="traceback">Traceback (most recent call last):\n  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/cherrypy/_cprequest.py", line 663, in respond\n    self.body.process()\n  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/cherrypy/_cpreqbody.py", line 996, in process\n    super(RequestBody, self).process()\n  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/cherrypy/_cpreqbody.py", line 540, in process\n    proc(self)\n  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/cherrypy/_cpreqbody.py", line 143, in process_urlencoded\n    qs = entity.fp.read()\n  File "/Library/Frameworks/Python.framework/Versions/3.4/lib/python3.4/site-packages/cherrypy/_cpreqbody.py", line 858, in read\n    return ntob(\'\').join(chunks)\nTypeError: sequence item 0: expected a bytes-like object, str found\n</pre>\n    <div id="powered_by">\n      <span>\n        Powered by <a href="http://www.cherrypy.org">CherryPy 3.8.0</a>\n      </span>\n    </div>\n    </body>\n</html>\n']

Failure
Traceback (most recent call last):
  File "/Users/jweob/PyCharmProjects/coheat-network-control/testUiWebserver.py", line 82, in test_echo
    response = self.webapp_request('/echo', method='POST', msg="hey there")
  File "/Users/jweob/PyCharmProjects/coheat-network-control/testUiWebserver.py", line 64, in webapp_request
    raise AssertionError("Unexpected error")
AssertionError: Unexpected error


Process finished with exit code 0

I think the error is something to do with how I am encoding the message in the POST body, but I can't work out exactly what I am doing wrong.

Aucun commentaire:

Enregistrer un commentaire