我在做什么
我正在通过构建一个 REST api 来学习 aiohttp,我正在使用 Pytest(及其 async 和 aiohttp 插件)对其进行测试。
对于我的第一个测试(我从一开始就使用 TDD),我有以下代码:
@pytest.mark.asyncio
async def test_handle_user_create(
aiohttp_client, init_test_app, create_test_user_table
):
payload = {
"email": "tintin@gmail.com",
"username": "Tintin",
"password": "y0u != n00b1e",
}
client = await aiohttp_client(init_test_app)
resp = await client.post("/users/", json=payload)
...
aiohttp_client
是来自的客户端夹具pytest-aiohttp
init_test_app
是一个基本上反映了我将要构建的应用程序的夹具create_test_user_table
是我在测试数据库中为用户创建表的装置
它出了什么问题
我的第一个测试是在上面代码块的最后一行抛出以下运行时错误:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
env36\lib\site-packages\aiohttp\test_utils.py:295: in request
method, self.make_url(path), **kwargs
env36\lib\site-packages\aiohttp\client.py:417: in _request
with timer:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <aiohttp.helpers.TimerContext object at 0x0000015DE23A3B38>
def __enter__(self) -> BaseTimerContext:
task = current_task(loop=self._loop)
if task is None:
> raise RuntimeError(
'Timeout context manager should be used '
'inside a task'
)
E RuntimeError: Timeout context manager should
be used inside a task
env36\lib\site-packages\aiohttp\helpers.py:568: RuntimeError
从错误消息中,我了解到客户端正在尝试使用异步超时上下文管理器,但这失败了,因为我没有在任务中调用它。
我不知道我的推论是否正确。
而且,我对 asyncio 不太满意,不知道如何解决这个问题。
如果有人告诉我出路,我将不胜感激。
更多信息
这是我的测试文件的源代码:
import asyncio
import sqlite3
from pathlib import Path
import pytest
from aiohttp import web
from app import router
@pytest.fixture(name="event_loop", scope="session")
def fixture_event_loop():
"""
Mock session scoped event loop.
Default event loop is function scoped, and won't work with
otherwisely scoped fixtures. Hence, the need for this overwrite.
"""
loop = asyncio.get_event_loop()
yield loop
loop.close()
@pytest.fixture(name="test_db_path", scope="session")
async def fixture_test_db_path():
return Path(__file__).absolute().parent.joinpath("test_db.sqlite")
@pytest.fixture(name="init_test_db_conn", scope="session")
async def fixture_init_test_db_conn(test_db_path):
"""
Mock initiator of test database connection.
"""
async def _init_test_db_conn(test_app):
with sqlite3.connect(str(test_db_path)) as conn:
test_app["DB_CONN"] = conn
yield
return _init_test_db_conn
@pytest.fixture(name="init_test_app", scope="session")
async def fixture_init_test_app(init_test_db_conn):
"""
Mock app for testing.
Substitute the test db for the development db for testing and
undo the substitution after all tests have been run.
"""
app = web.Application()
app.add_routes(router)
app.cleanup_ctx.append(init_test_db_conn)
return app
@pytest.fixture(name="create_test_user_table")
def fixture_create_test_user_table(test_db_path):
"""
Mock user table for tests. Scope at function level.
Drop table at end of each test.
"""
conn = sqlite3.connect(str(test_db_path))
conn.execute(
"""CREATE TABLE test_users (
id INTEGER PRIMARY KEY,
email TEXT NOT NULL UNIQUE,
username TEXT NOT NULL UNIQUE,
pwd_hash TEXT NOT NULL,
active INTEGER,
joined TEXT NOT NULL);
"""
)
yield
conn.execute("""DROP TABLE test_users;""")
@pytest.mark.asyncio
async def test_handle_user_create(
aiohttp_client, init_test_app, create_test_user_table
):
payload = {
"email": "tintin@gmail.com",
"username": "Tintin",
"password": "y0u != n00b1e",
}
client = await aiohttp_client(init_test_app)
resp = await client.post("/users/", json=payload)
assert resp.status == 200
resp_json = await resp.json()
assert resp_json["email"] == payload["email"]
assert resp_json["username"] == payload["username"]
assert resp_json["pwd_hash"] != payload["pwd_hash"]
assert resp_json["active"] == 0
client.close()
这是运行时错误的完整跟踪(以及弃用警告 - 我也会很感激获得帮助:))
$ pytest
============================= test session starts =============================
platform win32 -- Python 3.6.8, pytest-5.3.5, py-1.8.1, pluggy-0.13.1
rootdir: C:\Users\Mfonism\Codeville\AIOHttp\curious_me
plugins: aiohttp-0.3.0, asyncio-0.10.0
collected 1 item
test_app.py F [100%]
================================== FAILURES ===================================
_______________________ test_handle_user_create[pyloop] _______________________
aiohttp_client = <function aiohttp_client.<locals>.go at 0x0000015DE239AD08>
init_test_app = <Application 0x15de23a0d30>, create_test_user_table = None
@pytest.mark.asyncio
async def test_handle_user_create(
aiohttp_client, init_test_app, create_test_user_table
):
payload = {
"email": "tintin@gmail.com",
"username": "Tintin",
"password": "y0u != n00b1e",
}
client = await aiohttp_client(init_test_app)
> resp = await client.post("/users/", json=payload)
test_app.py:89:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
env36\lib\site-packages\aiohttp\test_utils.py:295: in request
method, self.make_url(path), **kwargs
env36\lib\site-packages\aiohttp\client.py:417: in _request
with timer:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
self = <aiohttp.helpers.TimerContext object at 0x0000015DE23A3B38>
def __enter__(self) -> BaseTimerContext:
task = current_task(loop=self._loop)
if task is None:
> raise RuntimeError('Timeout context manager should be used '
'inside a task')
E RuntimeError: Timeout context manager should be used inside a task
env36\lib\site-packages\aiohttp\helpers.py:568: RuntimeError
============================== warnings summary ===============================
test_app.py::test_handle_user_create[pyloop]
c:\users\mfonism\codeville\aiohttp\curious_me\env36\lib\site-packages\aiohttp\cookiejar.py:55: DeprecationWarning: The object should be created from async function
super().__init__(loop=loop)
test_app.py::test_handle_user_create[pyloop]
c:\users\mfonism\codeville\aiohttp\curious_me\env36\lib\site-packages\aiohttp\test_utils.py:247: DeprecationWarning: The object should be created from async function
**kwargs)
test_app.py::test_handle_user_create[pyloop]
c:\users\mfonism\codeville\aiohttp\curious_me\env36\lib\site-packages\aiohttp\connector.py:730: DeprecationWarning: The object should be created from async function
loop=loop)
test_app.py::test_handle_user_create[pyloop]
c:\users\mfonism\codeville\aiohttp\curious_me\env36\lib\site-packages\aiohttp\connector.py:735: DeprecationWarning: The object should be created from async function
resolver = DefaultResolver(loop=self._loop)
-- Docs: https://docs.pytest.org/en/latest/warnings.html
====== 1 failed, 4 warnings in 0.78s ======