使用AsyncHTTPTestCase为使用asyncio事件循环的现有Tornado应用程序编写测试时遇到一些麻烦。 在这里,我准备短模型,我可以重现问题:
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循环应用程序工作正常(我的意思是我可以做请求而我得到响应),但是像这样的测试失败并出现错误:
======================================================================
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()命令,第一次测试通过OK,但第二次总是失败。
当我将AsyncIOMainLoop()。install()移动到start_app()方法时 - 测试传递正常,但我担心在测试期间我使用一个事件循环,但在实际运行的应用程序中我使用asyncio循环。< / p>
因此,对于代码测试传递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()函数?
P.S。我已将self.io_loop.clear_instance()添加到tearDown() - 它看起来可能很脏,但适用于从make_app()代码调用AsyncIOMainLoop()。install()的情况。
def tearDown(self):
self.io_loop.clear_instance()
super().tearDown()
答案 0 :(得分:1)
根据文档,我需要在启动应用程序之前安装AsyncIOMainLoop,而不是在我制作应用程序时。 documentation
from tornado.platform.asyncio import AsyncIOMainLoop
import asyncio
AsyncIOMainLoop().install()
asyncio.get_event_loop().run_forever()
所以现在我确定正确的方法是将 AsyncIOMainLoop 安装到start_app()代码中。
所以现在我的模式代码如下:
<强> 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()。