{"id":198,"library":"pytest-asyncio","title":"pytest-asyncio","description":"pytest plugin for testing asyncio code. Required for async def test_ functions — pytest does not run them natively. Has undergone multiple breaking redesigns around event loop scoping. Current version is 1.3.0 (2026). Has a long history of yanked releases and sudden breaking changes.","status":"active","version":"1.3.0","language":"python","source_language":"en","source_url":"https://pytest-asyncio.readthedocs.io/en/stable/reference/changelog.html","tags":["testing","asyncio","async","pytest","fixtures"],"install":[{"cmd":"pip install pytest-asyncio","lang":"bash","label":"Python"}],"dependencies":[{"reason":"pytest-asyncio 1.x requires pytest 8+.","package":"pytest>=8.0","optional":false}],"imports":[{"note":"Async fixtures must use @pytest_asyncio.fixture, not @pytest.fixture. In strict mode, @pytest.fixture on an async function raises a warning/error. In auto mode it works but is still discouraged.","wrong":"@pytest.fixture\nasync def my_async_fixture():\n    return await setup_something()  # silently broken in strict mode","symbol":"pytest_asyncio.fixture","correct":"import pytest_asyncio\n\n@pytest_asyncio.fixture\nasync def my_async_fixture():\n    return await setup_something()"},{"note":"In strict mode (default since 0.21), async tests must be explicitly marked with @pytest.mark.asyncio or asyncio_mode='auto' must be set globally.","wrong":"async def test_something():\n    await asyncio.sleep(0)  # silently passes without running in strict mode","symbol":"asyncio mark","correct":"@pytest.mark.asyncio\nasync def test_something():\n    await asyncio.sleep(0)"}],"quickstart":{"code":"# pytest.ini or pyproject.toml — REQUIRED configuration\n# [pytest]\n# asyncio_mode = auto\n\n# With asyncio_mode=auto, no per-test marks needed:\nimport pytest_asyncio\n\n@pytest_asyncio.fixture\nasync def async_client():\n    client = await create_client()\n    yield client\n    await client.close()\n\nasync def test_something(async_client):\n    result = await async_client.fetch()\n    assert result is not None\n\n# Without asyncio_mode=auto, mark each test explicitly:\nimport pytest\n\n@pytest.mark.asyncio\nasync def test_explicit():\n    await asyncio.sleep(0)","lang":"python","description":"asyncio_mode must be configured. Use @pytest_asyncio.fixture for async fixtures."},"warnings":[{"fix":"Add to pytest.ini: [pytest]\\nasyncio_mode = auto\\n\\nOr use strict mode and mark every test with @pytest.mark.asyncio.","message":"asyncio_mode must be explicitly set. There is no safe default — omitting it causes a DeprecationWarning and the default changed from 'legacy' to 'strict' in 0.21. LLM-generated configs often omit this entirely.","severity":"breaking","affected_versions":"all"},{"fix":"Migrate to asyncio_mode=auto (least friction) or asyncio_mode=strict (explicit marks required).","message":"legacy mode removed in 0.23. Any codebase relying on asyncio_mode=legacy (the old default) will fail after upgrading past 0.21.","severity":"breaking","affected_versions":">= 0.23"},{"fix":"Use the loop_scope argument on @pytest.mark.asyncio or @pytest_asyncio.fixture instead: @pytest_asyncio.fixture(loop_scope='session'). For custom event loop policies, use the event_loop_policy fixture.","message":"Custom event_loop fixture redefinition removed in 1.x. A very common pattern in older tutorials — defining a session-scoped event_loop fixture — now raises an error.","severity":"breaking","affected_versions":">= 1.0"},{"fix":"Use @pytest.mark.asyncio(loop_scope='module') or @pytest.mark.asyncio(loop_scope='class') instead.","message":"@pytest.asyncio_event_loop mark removed. Was used for class/module-scoped event loops.","severity":"breaking","affected_versions":">= 1.0"},{"fix":"Always use @pytest_asyncio.fixture for async fixtures regardless of mode.","message":"Async fixtures must use @pytest_asyncio.fixture, not @pytest.fixture. Using @pytest.fixture on an async function works in auto mode but emits warnings and fails in strict mode.","severity":"gotcha","affected_versions":"all"},{"fix":"Use asyncio_mode=auto to avoid per-test marks, or ensure every async test has @pytest.mark.asyncio in strict mode.","message":"async def test_ functions silently appear to pass without actually running in strict mode if @pytest.mark.asyncio is missing. No error is raised — the test is collected and marked as passed.","severity":"gotcha","affected_versions":"all"},{"fix":"Use 1.x (current stable). If pinning, verify the specific version is not yanked on PyPI.","message":"Several versions in the 0.22-0.24 range were yanked from PyPI due to fundamental event loop scoping bugs. Pinning to a yanked version will fail at install time.","severity":"gotcha","affected_versions":"0.22.x, some 0.24.x"},{"fix":"Address pip warnings according to pip's recommendations, such as using a virtual environment or updating pip, if desired. This output does not indicate a pytest-asyncio specific failure.","message":"Test output contains pip-specific warnings/notices (e.g., about running as root or available updates). These are external to pytest-asyncio's behavior.","severity":"gotcha","affected_versions":"all"}],"env_vars":null,"last_verified":"2026-05-12T10:02:16.570Z","next_check":"2026-06-25T00:00:00.000Z","problems":[{"fix":"Install `pytest-asyncio` (`pip install pytest-asyncio`). The plugin automatically detects and runs `async def` tests.","cause":"`pytest` attempts to run an `async def` test function as a regular function because `pytest-asyncio` is not installed or correctly configured to manage async tests.","error":"RuntimeWarning: coroutine 'test_example' was never awaited"},{"fix":"Ensure all async operations are performed within `async def` test functions or `async` fixtures properly managed by `pytest-asyncio`.","cause":"An asynchronous function or coroutine is being called or awaited outside an active `asyncio` event loop managed by `pytest-asyncio`.","error":"RuntimeError: There is no current event loop in thread 'main'"},{"fix":"Review the scopes of `pytest_asyncio.fixture` definitions, ensuring dependent fixtures have compatible scopes (e.g., a session-scoped fixture cannot depend on a function-scoped fixture).","cause":"An `asyncio`-scoped fixture depends on or is configured with a scope incompatible with another `asyncio`-scoped fixture it interacts with.","error":"pytest_asyncio.plugin.ScopeMismatch"},{"fix":"Remove explicit usage of the `loop` fixture. `pytest-asyncio` typically manages the event loop implicitly for `async def` tests. For explicit backend control, use the `anyio_backend` fixture or configure `asyncio_mode` in `pytest.ini`.","cause":"The test suite or fixtures are explicitly using the `loop` fixture, which has been deprecated in newer `pytest-asyncio` versions in favor of more modern event loop management, often via `anyio`.","error":"DeprecationWarning: The `loop` fixture is deprecated. Use `anyio_backend` or `asyncio_mode` instead."}],"ecosystem":"pypi","meta_description":null,"install_score":100,"install_tag":"verified","quickstart_score":80,"quickstart_tag":"verified","pypi_latest":null,"install_checks":{"last_tested":"2026-05-12","tag":"verified","tag_description":"installs cleanly on critical runtimes, fast import, recently tested","results":[{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.48,"mem_mb":14.1,"disk_size":"30.6M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.33,"mem_mb":14.1,"disk_size":"31M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.68,"mem_mb":15.4,"disk_size":"33.8M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.54,"mem_mb":15.4,"disk_size":"34M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.82,"mem_mb":15.4,"disk_size":"25.4M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.81,"mem_mb":15.4,"disk_size":"26M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.81,"mem_mb":15.2,"disk_size":"24.7M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.75,"mem_mb":15.2,"disk_size":"25M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.44,"mem_mb":13.3,"disk_size":"30.0M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.41,"mem_mb":13.3,"disk_size":"30M"}]},"quickstart_checks":{"last_tested":"2026-04-23","tag":"verified","tag_description":"quickstart runs on critical runtimes, recently tested","results":[{"runtime":"python:3.10-alpine","exit_code":0},{"runtime":"python:3.10-slim","exit_code":0},{"runtime":"python:3.11-alpine","exit_code":0},{"runtime":"python:3.11-slim","exit_code":0},{"runtime":"python:3.12-alpine","exit_code":0},{"runtime":"python:3.12-slim","exit_code":0},{"runtime":"python:3.13-alpine","exit_code":0},{"runtime":"python:3.13-slim","exit_code":0},{"runtime":"python:3.9-alpine","exit_code":0},{"runtime":"python:3.9-slim","exit_code":0}]}}