如何在 Python 3 中对 CherryPy webapp 进行单元测试?
How to unittest a CherryPy webapp in Python 3?
我正在尝试在 CherryPy 中对 Web 应用程序进行单元测试。我发现 this answer 这正是我想要做的,但它是写在 Python 2 中的,我在 Python 3 中工作时遇到了一些问题。这是我修改过的代码:
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()
我从服务器收到 POST 测试的意外错误:
/Library/Frameworks/Python.framework/Versions/3.4/bin/python3.4 /Applications/PyCharm.app/Contents/helpers/pycharm/utrunner.py /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://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\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
我认为该错误与我在 POST 正文中对消息进行编码的方式有关,但我无法确定我做错了什么。
我在这里找到了提示 here。我应该使用 io.BytesIO 而不是 io.StringIO,而不是
fd = StringIO(qs)
我用过
fd = BytesIO(qs.encode())
进行此更改后,该示例可以正常运行。
我正在尝试在 CherryPy 中对 Web 应用程序进行单元测试。我发现 this answer 这正是我想要做的,但它是写在 Python 2 中的,我在 Python 3 中工作时遇到了一些问题。这是我修改过的代码:
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()
我从服务器收到 POST 测试的意外错误:
/Library/Frameworks/Python.framework/Versions/3.4/bin/python3.4 /Applications/PyCharm.app/Contents/helpers/pycharm/utrunner.py /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://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\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
我认为该错误与我在 POST 正文中对消息进行编码的方式有关,但我无法确定我做错了什么。
我在这里找到了提示 here。我应该使用 io.BytesIO 而不是 io.StringIO,而不是
fd = StringIO(qs)
我用过
fd = BytesIO(qs.encode())
进行此更改后,该示例可以正常运行。