异步单元测试Sanic应用程序抛出运行时错误:此事件循环已在运行

2024-05-09 23:00:27 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个Sanic应用程序,可以对外部api进行异步调用。我希望编写一些模拟这些外部调用的单元测试。在

在下面的代码中,我们可以从日志中看到测试通过了。但是,在它们完成一个RuntimeError之后:抛出这个已经在运行的事件循环

简化的Sanic应用程序:

app = Sanic(__name__)
app.config.from_pyfile('/usr/src/app/config.py')
Initialize(
    app,
    access_token_name='jwt',
    authenticate=lambda: True,
    claim_aud=app.config.AUTH_JWT_TOKEN['service']['audience'],
    claim_iss=app.config.AUTH_JWT_TOKEN['service']['issuer'],
    public_key=app.config.AUTH_JWT_TOKEN['service']['secret'],
    responses_class=JWTResponses
)


@app.listener('before_server_start')
def init(app, loop):
    ssl_ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
    ssl_ctx.load_cert_chain(app.config.SSL_CERT, app.config.SSL_CERT_KEY)
    ssl_ctx.load_verify_locations(app.config.SSL_SERVER_CERT)
    ssl_ctx.check_hostname = False
    ssl_ctx.verify_mode = ssl.CERT_REQUIRED
    conn = aiohttp.TCPConnector(ssl_context=ssl_ctx)
    app.aiohttp_session = aiohttp.ClientSession(loop=loop, connector=conn)
    access_logger.disabled = True


@app.listener('after_server_stop')
def finish(app, loop):
    loop.run_until_complete(app.aiohttp_session.close())
    loop.close()


@app.route("endpoint/<mpn>")
@protected()
async def endpoint(request, mpn):
    msg = msg(
        mpn,
    )
    headers = {'content-type': 'text/xml'}
    async with session.post(
        config.URL,
        data=msg.tostring(pretty_print=True, encoding='utf-8'),
        headers=headers,
    ) as response:
        response_text = await response.text()
        try:
            response = (
                Response.from_xml(response_text)
            )
            return response
        except ResponseException:
            logger.error(e.get_message()['errors'][0]['message'])
            return response.json(
                e.get_message(),
                status=HTTPStatus.INTERNAL_SERVER_ERROR
            )


if __name__ == '__main__':
    app.run(host="0.0.0.0", port=8000)

测试如下:

^{pr2}$

如上所述,测试确实通过,但随后抛出错误。在

================================================================================================= ERRORS ==================================================================================================
____________________________________________________________________________________ ERROR at teardown of test_success ____________________________________________________________________________________

tp = <class 'RuntimeError'>, value = None, tb = None

    def reraise(tp, value, tb=None):
        try:
            if value is None:
                value = tp()
            if value.__traceback__ is not tb:
                raise value.with_traceback(tb)
>           raise value
/usr/local/lib/python3.6/site-packages/six.py:693:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/usr/local/lib/python3.6/site-packages/six.py:693: in reraise
    raise value
/usr/local/lib/python3.6/site-packages/six.py:693: in reraise
    raise value
/usr/local/lib/python3.6/site-packages/pytest_sanic/plugin.py:212: in sanic_client
    loop.run_until_complete(client.close())
uvloop/loop.pyx:1451: in uvloop.loop.Loop.run_until_complete
    ???
/usr/local/lib/python3.6/site-packages/pytest_sanic/utils.py:230: in close
    await self._server.close()
/usr/local/lib/python3.6/site-packages/pytest_sanic/utils.py:134: in close
    await trigger_events(self.after_server_stop, self.loop)
/usr/local/lib/python3.6/site-packages/pytest_sanic/utils.py:25: in trigger_events
    result = event(loop)
server.py:84: in finish
    loop.run_until_complete(app.aiohttp_session.close())
uvloop/loop.pyx:1445: in uvloop.loop.Loop.run_until_complete
    ???
uvloop/loop.pyx:1438: in uvloop.loop.Loop.run_until_complete
    ???
uvloop/loop.pyx:1347: in uvloop.loop.Loop.run_forever
    ???
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

>   ???
E   RuntimeError: this event loop is already running.

uvloop/loop.pyx:448: RuntimeError

如有任何帮助或建议,我们将不胜感激。提前谢谢


Tags: runinpyloopconfigappsslclose
2条回答

您也可以在您的test_cli上注销对after_server_stop的调用:

test_cli.server.after_server_stop = []

我认为您可以通过更改此方法来修复错误:

@app.listener('after_server_stop')
async def finish(app, loop):
    await app.aiohttp_session.close()

关闭循环不是您的责任,您应该使用这样一个事实,即finish是从异步上下文调用的(不需要启动事件循环,它已经在运行)。在

如果这不是唯一的问题,那么也许从一个简单的例子开始,添加一些东西,直到它再次崩溃。Sanic似乎有一个很好的文档test section,他们建议使用pytest-sanic。使用pytest的一个简单示例如下:

^{pr2}$
# file: server_test.py
import pytest
from server import app

@pytest.yield_fixture
def sanic_app():
    yield app

@pytest.fixture
def test_cli(loop, sanic_app, sanic_client):
    return loop.run_until_complete(sanic_client(app))

async def test_index(test_cli):
    resp = await test_cli.get('/')
    assert resp.status == 200
    json = await resp.json()
    assert json == {'hello': 'world'}

async def test_index_fail(test_cli):
    resp = await test_cli.get('/')
    assert resp.status == 200
    json = await resp.json()
    assert json == {'bonjour': 'monde'}

您需要安装一些软件包:

pip install sanic pytest pytest-sanic

然后可以运行pytest,第一个测试通过,第二个测试失败。在

一般来说,您不必自己启动事件循环,始终尝试摆脱loop.run_...。在

相关问题 更多 >