【问题标题】:Unittesting cherrypy webapp单元测试cherrypy webapp
【发布时间】:2012-12-24 23:15:23
【问题描述】:

我最近不得不重写我们的 rest api,并从 Flask 切换到 Cherrypy(主要是由于 Python 3 兼容性)。但是现在我一直在尝试编写单元测试,Flask 有一个非常漂亮的内置测试客户端,您可以使用它向您的应用程序发送虚假请求(无需启动服务器。)我找不到任何类似的功能对于 Cherrypy,是否有这样的功能,还是我卡在启动服务器并针对它执行实际请求?

【问题讨论】:

    标签: python unit-testing cherrypy


    【解决方案1】:

    据我所知,CherryPy 确实没有为这种类型的测试提供工具(没有运行的服务器)。但它仍然很容易做到(尽管它依赖于 CherryPy 的一些内部结构)。

    这是一个简单的展示:

    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()
    

    编辑,我已将此答案扩展为CherryPy recipe

    【讨论】:

    • 谢谢,今天就试试这个。
    • @Sylvain:@cherrypy.tools.json_in() 支持有什么特别需要的吗?测试用例中的cherrypy.request.json 导致AttributeError: 'Request' object has no attribute 'json'
    • 启用该工具就足够了,但也许不是通过装饰器。您是否尝试过配置字典?
    • @SylvainHellegouarch 在你的装置(包括进出)中有一个 json 示例真是太好了。这仍然非常相关,cherrypy 应该正式支持这一点:)
    • @SylvainHellegouarch 你能更新你的 CherryPy 食谱回购链接吗?谢谢
    【解决方案2】:

    似乎有另一种方法来执行单元测试。 我刚刚找到并检查了以下适用于cherrypy 3.5的配方。

    http://docs.cherrypy.org/en/latest/advanced.html#testing-your-application

        import cherrypy
    
        from cherrypy.test import helper
    
        class SimpleCPTest(helper.CPWebCase):
            def setup_server():
                class Root(object):
                    @cherrypy.expose
                    def echo(self, message):
                        return message
    
                cherrypy.tree.mount(Root())
            setup_server = staticmethod(setup_server)
    
            def test_message_should_be_returned_as_is(self):
                self.getPage("/echo?message=Hello%20world")
                self.assertStatus('200 OK')
                self.assertHeader('Content-Type', 'text/html;charset=utf-8')
                self.assertBody('Hello world')
    
            def test_non_utf8_message_will_fail(self):
                """
                CherryPy defaults to decode the query-string
                using UTF-8, trying to send a query-string with
                a different encoding will raise a 404 since
                it considers it's a different URL.
                """
                self.getPage("/echo?message=A+bient%F4t",
                             headers=[
                                 ('Accept-Charset', 'ISO-8859-1,utf-8'),
                                 ('Content-Type', 'text/html;charset=ISO-8859-1')
                             ]
                )
                self.assertStatus('404 Not Found')
    

    【讨论】:

    • 这当然是 CherryPy 测试内部编写的方式。但这意味着启动一个实际的服务器。这是自动的,但我的食谱试图表明你可以在没有运行服务器的情况下测试你的处理程序。我想说的是,在集成方面,您的回答并没有错。
    【解决方案3】:

    我发现 Sylvain Hellegouarch 的答案对解决这个问题非常有帮助,但它使用 Python 2。我调整了他们的答案以使用 Python 3:

    import io
    import unittest
    import urllib
    import urllib.parse
    
    import cherrypy
    from cherrypy.lib import httputil
    
    local = httputil.Host('127.0.0.1', 50000, '')
    remote = 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', f'{len(qs)}'))
                fd = io.BytesIO(qs.encode())
                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'])
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-08-09
      • 2011-02-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-15
      相关资源
      最近更新 更多