Pytest Trio Plugin
pytest-trio is a pytest plugin designed to facilitate testing projects that leverage Trio, a friendly library for concurrency and async I/O in Python. It enables writing asynchronous tests without boilerplate, provides useful Trio-specific fixtures like `nursery` and `autojump_clock`, and supports async fixtures. The current stable version is 0.8.0, and it follows a release cadence tied to Trio and pytest compatibility updates.
Warnings
- breaking If a `yield` in an async fixture (decorated with `@pytest.fixture` or `@pytest_trio.trio_fixture`) is cancelled, it will raise `trio.Cancelled` and teardown code after the `yield` might not execute. This differs significantly from standard pytest fixtures where `yield` never raises an exception. Ensure critical cleanup is in a `finally` block.
- breaking Trio 0.22.0 deprecated `trio.MultiError` in favor of Python's standard `BaseExceptionGroup` (PEP 654). `pytest-trio` 0.8.0 has been updated to use `ExceptionGroup` exclusively, requiring `trio>=0.22.0`. Older versions of `pytest-trio` with newer `trio` might encounter deprecation warnings or compatibility issues.
- gotcha Without `trio_mode = true` in `pytest.ini`, `pytest-trio` only processes tests and fixtures explicitly marked with `@pytest.mark.trio` or `@pytest_trio.trio_fixture`. This can lead to async tests being skipped or not running correctly if not explicitly marked.
- gotcha `pytest-trio` runs `trio.run()` for each individual test, creating a fresh Trio environment. This differs from how standard pytest fixtures might be set up once for multiple tests, and can occasionally surprise users expecting a single shared Trio event loop across a test module or class.
- breaking Support for Python 3.5 and 3.6 was removed in older versions of `pytest-trio`. Current versions require Python 3.7 or newer.
- gotcha There are reports that `pytest-trio` tests may fail with `pytest` 8.4 due to empty tracebacks when the plugin raises an exception directly. This indicates a potential compatibility issue with newer `pytest` versions.
Install
-
pip install pytest-trio
Imports
- pytest.mark.trio
import pytest @pytest.mark.trio async def test_something(): ... - pytest_trio.trio_fixture
from pytest_trio import trio_fixture @trio_fixture async def my_fixture(): ... - nursery
async def test_with_nursery(nursery): await nursery.start(some_task) - autojump_clock
async def test_with_clock(autojump_clock): await trio.sleep(1)
Quickstart
# pytest.ini
[pytest]
trio_mode = true
# test_example.py
import trio
async def test_sleep():
start_time = trio.current_time()
await trio.sleep(0.01) # Use a small sleep for quick tests
end_time = trio.current_time()
assert end_time - start_time >= 0.01
async def test_should_pass_with_fixture(nursery):
results = []
async def background_task():
await trio.sleep(0.001)
results.append('done')
nursery.start_soon(background_task)
await trio.sleep(0.002) # Ensure background task has time to run
assert 'done' in results
# Run with: pytest test_example.py