有一种方法可以使用固定装置进行超时,只需将以下钩子添加到conftest.py
.
- 任何带有前缀的fixture必须返回测试可以运行
timeout
的秒数( int
, )。float
- 选择最接近的夹具 wrt 范围。
autouse
固定装置的优先级低于明确选择的固定装置。后一种是首选。不幸的是,函数参数列表中的顺序并不重要。
- 如果没有这样的夹具,则测试不受限制,将像往常一样无限期地运行。
- 测试也必须标记为
pytest.mark.asyncio
,但无论如何都需要。
# Add to conftest.py
import asyncio
import pytest
_TIMEOUT_FIXTURE_PREFIX = "timeout"
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_setup(item: pytest.Item):
"""Wrap all tests marked with pytest.mark.asyncio with their specified timeout.
Must run as early as possible.
Parameters
----------
item : pytest.Item
Test to wrap
"""
yield
orig_obj = item.obj
timeouts = [n for n in item.funcargs if n.startswith(_TIMEOUT_FIXTURE_PREFIX)]
# Picks the closest timeout fixture if there are multiple
tname = None if len(timeouts) == 0 else timeouts[-1]
# Only pick marked functions
if item.get_closest_marker("asyncio") is not None and tname is not None:
async def new_obj(*args, **kwargs):
"""Timed wrapper around the test function."""
try:
return await asyncio.wait_for(
orig_obj(*args, **kwargs), timeout=item.funcargs[tname]
)
except Exception as e:
pytest.fail(f"Test {item.name} did not finish in time.")
item.obj = new_obj
例子:
@pytest.fixture
def timeout_2s():
return 2
@pytest.fixture(scope="module", autouse=True)
def timeout_5s():
# You can do whatever you need here, just return/yield a number
return 5
async def test_timeout_1():
# Uses timeout_5s fixture by default
await aio.sleep(0) # Passes
return 1
async def test_timeout_2(timeout_2s):
# Uses timeout_2s because it is closest
await aio.sleep(5) # Timeouts
警告
可能不适用于其他一些插件,我只测试过它,如果被某个钩子重新定义pytest-asyncio
,它肯定不会起作用。item