AssertionError "assert not IOLoop.initialized()";使用 AsyncIOMainLoop 测试 Tornado 应用程序 [英] AssertionError "assert not IOLoop.initialized()" on testing Tornado app with AsyncIOMainLoop

查看:22
本文介绍了AssertionError "assert not IOLoop.initialized()";使用 AsyncIOMainLoop 测试 Tornado 应用程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在使用 AsyncHTTPTestCase 为使用异步事件循环的现有 Tornado 应用程序编写测试时遇到了一些麻烦.在这里,我准备了可以重现问题的短模型:

I have some trouble with writing tests with AsyncHTTPTestCase for existing Tornado application that uses asyncio event loop. Here I prepare short model where I can reproduce issue:

app.py

from tornado.platform.asyncio import AsyncIOMainLoop
import asyncio
import tornado.web


class MainHandler(tornado.web.RequestHandler):
    async def get(self, *args, **kwargs):
        self.write("200 OK")

    async def post(self, *args, **kwargs):
        self.write("201 OK")

def make_app():
    AsyncIOMainLoop().install()  # here is how to asyncio loop installed in app I already have
    return tornado.web.Application([
        (r"/", MainHandler),
    ], debug=True)

def start_app():
    app = make_app()
    app.listen(8888)
    loop = asyncio.get_event_loop()
    loop.set_debug(True)
    loop.run_forever()

start.py

#!/usr/bin/env python3
import app

if __name__ == "__main__":
    app.start_app()

test_app.py

import json
from tornado.testing import AsyncHTTPTestCase

import app

class TestHelloApp(AsyncHTTPTestCase):
    def get_app(self):
        return app.make_app()

    def test_get(self):
        response = self.fetch('/')
        self.assertEqual(response.code, 200)
        self.assertEqual(response.body.decode(), '200 OK')

    def test_post(self):
        response = self.fetch('/', method="POST",
                              body=json.dumps({"key": "value"}))
        self.assertEqual(response.code, 200)
        self.assertEqual(response.body.decode(), '201 OK')

使用这种安装 asyncio 循环应用程序的方法工作正常(我的意思是我可以执行请求并收到响应),但是这样的测试失败并出现错误:

With that approach of installation asyncio loop application works fine (I mean I can do requests and I'm getting responses), but test like this failed with error:

======================================================================
FAIL: test_post (test_app.TestHelloApp)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/biceps/work/torn/.venv/lib/python3.6/site-packages/tornado/testing.py", line 380, in setUp
    self._app = self.get_app()
  File "/home/biceps/work/torn/test_app.py", line 8, in get_app
    return app.make_app()
  File "/home/biceps/work/torn/app.py", line 14, in make_app
    tornado.platform.asyncio.AsyncIOMainLoop().install()
  File "/home/biceps/work/torn/.venv/lib/python3.6/site-packages/tornado/ioloop.py", line 181, in install
    assert not IOLoop.initialized()
AssertionError

----------------------------------------------------------------------
Ran 2 tests in 0.006s

FAILED (failures=1)

似乎由 AsyncIOMainLoop().install() 命令安装的循环在测试之间没有停止,第一个测试通过正常,但第二个总是失败.

Seems like loop that was installed by AsyncIOMainLoop().install() command is not stopped between tests, first test passed OK, but second always failed.

当我将 AsyncIOMainLoop().install() 移动到 start_app() 方法时 - 测试通过正常,但我担心在测试期间我使用一个事件循环,但在实际运行的应用程序中我使用 asyncio 循环.

When I moved AsyncIOMainLoop().install() to start_app() method - tests are passed OK, but I'm worrying about that during test I use one event loop, but in real running app I use asyncio loop.

因此,针对该代码测试通过 OK:

So, against that code tests are passed OK:

from tornado.platform.asyncio import AsyncIOMainLoop
import asyncio
import tornado.web


class MainHandler(tornado.web.RequestHandler):
    async def get(self, *args, **kwargs):
        self.write("200 OK")

    async def post(self, *args, **kwargs):
        self.write("201 OK")

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ], debug=True)

def start_app():
    AsyncIOMainLoop().install()
    app = make_app()
    app.listen(8888)
    loop = asyncio.get_event_loop()
    loop.set_debug(True)
    loop.run_forever()

问:我的问题是 - 如何在该用例中正确编写测试?当 Tornado 应用程序使用 AsyncIOMainLoop 时,如何使用 AsyncHTTPTestCase 编写测试?我决定将 AsyncIOMainLoop().install() 变成 start_app(),而不是在 make_app() 函数中吗?

Q: My question is - how to do write tests in that usecase correctly ? How to write tests with AsyncHTTPTestCase when Tornado app uses AsyncIOMainLoop ? Am I right with decision to make AsyncIOMainLoop().install() into start_app(), not in make_app() function ?

附言我已将 self.io_loop.clear_instance() 添加到 tearDown() - 它看起来可能很脏,但适用于从 make_app() 代码调用 AsyncIOMainLoop().install() 的情况.

P.S. I've added self.io_loop.clear_instance() to tearDown() - it looks probably dirty but that works for case when AsyncIOMainLoop().install() called from make_app() code.

    def tearDown(self):
    self.io_loop.clear_instance()
    super().tearDown()

推荐答案

根据文档,我需要在启动应用程序之前安装 AsyncIOMainLoop,而不是在我制作应用程序时.文档

According to documentation I need to install AsyncIOMainLoop before startup application, not when I'm making app. documentation

from tornado.platform.asyncio import AsyncIOMainLoop
import asyncio
AsyncIOMainLoop().install()
asyncio.get_event_loop().run_forever()

所以现在我确定正确的方法是使用 AsyncIOMainLoop 安装到 start_app() 代码中.

So now I'm sure that proper way is using AsyncIOMainLoop installation into start_app() code.

所以现在我的模式代码看起来像:

So now my pattern code looks like:

web1.py

class MainHandler(tornado.web.RequestHandler):
    async def get(self, *args, **kwargs):
        await asyncio.sleep(1)
        return self.write("OK")

    async def post(self, *args, **kwargs):
        await asyncio.sleep(1)
        return self.write("OK")

def make_app():
    return tornado.web.Application([(r"/", MainHandler),],
                                   debug=False)

def start_app():
    from tornado.platform.asyncio import AsyncIOMainLoop
    import asyncio
    AsyncIOMainLoop().install()
    app = make_app()
    app.listen(8888)
    asyncio.get_event_loop().run_forever()


if __name__ == "__main__":
    start_app()

test_app.py

from tornado.testing import AsyncHTTPTestCase

import web1

class TestTornadoAppBase(AsyncHTTPTestCase):
    def get_app(self):
        return web1.make_app()

    def get_new_ioloop(self):
        """
        Needed to make sure that I can also run asyncio based callbacks in my tests
        """
        io_loop = tornado.platform.asyncio.AsyncIOLoop()
        asyncio.set_event_loop(io_loop.asyncio_loop)
        return io_loop

class TestGET(TestTornadoAppBase):
    def test_root_get_method(self):
        response = self.fetch("/")
        self.assertEqual(response.code, 200)
        self.assertEqual(response.body.decode(), 'OK')

    def test_root_post_method(self):
        response = self.fetch("/", method="POST", body="{}")
        self.assertEqual(response.code, 200)
        self.assertEqual(response.body.decode(), 'OK')

这种模式也有效,并且在测试期间使用 AsyncIOMainLoop.所以我可以使用那些使用 asyncio 循环的库.在我的示例中,例如有 asyncio.sleep().

This pattern works as well, and during tests AsyncIOMainLoop is used. So I can use libraries those use asyncio loop. In my example there is asyncio.sleep() for example.

这篇关于AssertionError "assert not IOLoop.initialized()";使用 AsyncIOMainLoop 测试 Tornado 应用程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆