使用tornado进行测试AsyncHTTPTestCase连接超时

时间:2016-12-29 10:44:11

标签: python unit-testing tornado tornado-motor

我有单元测试龙卷风应用程序的问题,请帮助我。错误堆栈跟踪:

  

错误回溯(最近一次调用最后一次):文件   “/Users/doc/python/lib/python3.5/site-packages/tornado/testing.py”   第432行,在tearDown中       timeout = get_async_test_timeout())文件“/Users/doc/python-virt1/lib/python3.5/site-packages/tornado/ioloop.py”,   第456行,在run_sync中       提高TimeoutError('%s秒'%超时后操作超时)tornado.ioloop.TimeoutError:5之后操作超时   秒

     

错误:tornado.application:从未检索到未来的异常:Traceback(最近的   最后打电话):文件   “/Users/doc/python/lib/python3.5/site-packages/tornado/gen.py”   第1021行,在运行中       yielded = self.gen.throw(* exc_info)文件“/Library/Frameworks/Python.framework/Versions/3.5/lib/python3.5/types.py”,   第179行,投掷       return self .__ wrapped.throw(tp,* rest)File“/Users/doc/python-virt1/lib/python3.5/site-packages/tornado/gen.py”,   第1015行,在运行中       value = future.result()文件“/Users/doc/python-virt1/lib/python3.5/site-packages/tornado/concurrent.py”,   第237行,结果       raise_exc_info(self._exc_info)文件“”,第3行,在raise_exc_info tornado.curl_httpclient.CurlError:HTTP 599:空   从服务器回复Traceback(最近一次调用最后一次):

test.py文件:

from tornado.testing import gen_test
from tests.api_tests.base import AbstractApplicationTestBase


class ApiRestTest(AbstractApplicationTestBase):
    def setUp(self):
        super(ApiRestTest, self).setUp()
        self.prepareDatabase(self.config)
        self.insert_user(config=self.config)

api_test / base.py

import logging
from api import server
from commons.constants import config
from tests.base import BaseTestClass


class AbstractApplicationTestBase(BaseTestClass):
    def get_app(self):
        application = server.get_application(self.config)

        application.settings[config.APPLICATION_DB] = self.db
        application.settings[config.APPLICATION_CONFIG] = self.config
        application.settings[config.APPLICATION_AES] = self.aes
        application.settings[config.APPLICATION_FS] = self.fs
        logging.info(self.config.DEPLOY_API)

        return application

测试/ base.py

import logging
from datetime import datetime
import motor.motor_tornado
from motor import MotorGridFSBucket
from pymongo import MongoClient
from tornado import escape
from tornado import gen
from tornado.testing import AsyncHTTPTestCase

class BaseTestClass(AsyncHTTPTestCase):
     @classmethod
     def setUpClass(self):
         super(BaseTestClass, self).setUpClass()
         self.config = Config(Environment.TESTS.value)
         self.client = utils.http_client(self.config.PROXY_HOST, self.config.PROXY_PORT)
         self.db = motor.motor_tornado.MotorClient(self.config.MONGODB_URI)[self.config.MONGODB_NAME]
         self.fs = MotorGridFSBucket(self.db)

2 个答案:

答案 0 :(得分:2)

AsyncHTTPTestCase在每次测试开始时创建一个新的IOLoop,并在每次测试结束时销毁它。但是,您在整个测试类的开头创建了一个MotorClient,并使用默认的全局IOLoop而不是专门为每个测试创建的IOLoop。

我相信你只需要用setUp替换setUpClass。然后,在AsyncHTTPTestCase设置其IOLoop后,您将创建MotorClient。为清楚起见,明确传递IOLoop:

client = MotorClient(io_loop=self.io_loop)
self.db = client[self.config.MONGODB_NAME]

答案 1 :(得分:1)

我注意到的一些事情。

  • 我看到的主要问题是你在setUp方法中通过motor运行了一些IO,setUp不能是gen_test(AFAIK)。如果您需要这种类型的功能,您可能需要下拉到pymongo并同步调用数据库来存根这些数据库夹具。
  • 您是否故意针对真实数据库运行?这些应该是与mongodb真正的集成测试吗?当我编写这些类型的测试时,我通常会使用Mock类并模拟我与MongoDb的所有交互。
  • 此外,创建AsyncHttpClient对象没有任何成本,因此将其从您的settings / config对象传递到每个处理程序可能不是最佳做法。

这是我的项目中的示例处理程序测试:

example_fixture = [{'foo': 'bar'}]
URL = r'/list'

    class BaseListHandlerTests(BaseHandlerTestCase):
        """
        Test the abstract list handler
        """
        def setUp(self):
            self.mongo_client = Mock()
            self.fixture = deepcopy(example_fixture)
            # Must be run last
            BaseHandlerTestCase.setUp(self)

        def get_app(self):
            return Application([
                (URL, BaseListHandler,
                 dict(mongo_client=self.mongo_client))
            ], **settings)

        def test_get_list_of_objects_returns_200_with_results(self):
            self.mongo_client.find.return_value = self.get_future(example_fixture)
            response = self.fetch('{}'.format(URL))
            response_json = self.to_json(response)
            self.assertListEqual(response_json.get('results'), example_fixture)
            self.assertEqual(response.code, 200)

        def test_get_list_of_objects_returns_200_with_no_results(self):
            self.mongo_client.find.return_value = self.get_future([])
            response = self.fetch('{}'.format(URL))
            self.assertEqual(response.code, 200)

        def test_get_list_of_objects_returns_500_with_exception(self):
            self.mongo_client.find.return_value = self.get_future_with_exception(Exception('FAILED!'))
            response = self.fetch('{}'.format(URL))
            self.assertEqual(response.code, 500)

使这项工作的关键是我的mongo_client被传递到路由对象本身。所以我的处理程序初始化需要一个mongo_client kwarg。

class BaseListHandler(BaseHandler):
    """
    Base list handler
    """
    mongo_client = None

    def initialize(self, mongo_client=None):
        """
        Rest Client Initialize
        Args:
            mongo_client: The client used to access documents for this handler

        Returns:

        """
        BaseHandler.initialize(self)
        self.mongo_client = mongo_client