Async Tests
Scenario
You write an `async def test_my_func():` and pytest skips it, or gives a warning about an un-awaited coroutine.
Mental model
Pytest is a synchronous runner by default. If a test is a coroutine (`async def`), pytest just calls it (which returns a coroutine object) and immediately moves on without running it on an event loop. You need a plugin to provide the event loop.
Deep dive
To test `async` functions, you must install `pytest-asyncio`. This plugin automatically provisions an event loop for your tests.
Any test defined as `async def` must be decorated with `@pytest.mark.asyncio`, or you must set `asyncio_mode = auto` in your `pytest.ini` so it detects them automatically.
Code examples
Failing: un-awaited coroutine
async def test_fetch_data():
data = await fetch_data()
assert data is not None
# Pytest will warn: coroutine 'test_fetch_data' was never awaited
Standard pytest doesn't know how to run an event loop to execute the coroutine.
Working Async Test
import pytest
@pytest.mark.asyncio
async def test_fetch_data():
data = await fetch_data()
assert data['status'] == 'ok'
With `pytest-asyncio`, the decorator tells pytest to spin up an event loop, run the coroutine to completion, and tear down the loop.
Common mistakes
- Mixing sync test client with async code: If your application relies heavily on async features, testing it with the synchronous `TestClient` can mask async blocking bugs. Use `httpx.AsyncClient` instead.
- Async fixtures without pytest-asyncio: Just like tests, if you define an `async def` fixture, `pytest-asyncio` is required to evaluate it properly.
Recall questions
- Why does a standard pytest run fail to execute `async def` tests?
- What plugin is standard for running async tests in pytest?
- How can you avoid decorating every async test with `@pytest.mark.asyncio`?
Questions & answers
If you have an `async` database driver, how must you write your test fixture to provide a session?
The fixture must be an `async def` function, yielding the async session. The tests consuming it must also be `async def` and marked with `@pytest.mark.asyncio`.
What happens if an `async` test hangs indefinitely?
Usually it means a task was left awaiting a lock or a network call that never resolved, or there is a sync blocking call starving the test's event loop.
Continue learning
Previous: Pytest Basics and Fixtures