pytest-asyncio: Running coroutine tests with pytest
Asyncio support for pytest
At a glance
- What is it?
- pytest-asyncio is a pytest plugin for Python developers who need to await asyncio code inside test functions. It supports Python 3.10 and newer, but excludes test classes that directly subclass unittest.TestCase.
- Who is it for?
- pytest-asyncio fits Python projects that test asyncio coroutines with pytest and use a supported interpreter and pytest release. It is not suitable for standard unittest.TestCase subclasses.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 7 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The coroutine testing gap addressed by pytest-asyncio
pytest-asyncio addresses a narrow but common testing problem: pytest needs plugin support to treat a Python coroutine test function as an executable test item and await it. Without that support, declaring an `async def` test does not by itself establish the execution behavior required by the project.
The intended users are Python developers testing applications, libraries, or services built around Python's `asyncio` library. Their tests need to call asynchronous operations and inspect returned values directly with `await`. The README gives a coroutine marked with `@pytest.mark.asyncio`, followed by an awaited call to `library.do_something()` and an assertion against its result.
This is not a general asynchronous application framework. Its documented purpose is pytest support for `asyncio`, specifically coroutines used as test functions. The project also does not cover every possible unittest integration. Test classes that subclass the standard `unittest.TestCase` class are explicitly unsupported. For that case, the README directs developers to `unittest.IsolatedAsyncioTestCase` or an async framework such as `asynctest`.
That boundary matters during adoption. A project that keeps its asynchronous tests in pytest-style functions can use the plugin directly. A project built around ordinary `unittest.TestCase` subclasses needs another approach even though both environments may describe their tests as unit tests. The plugin changes how pytest runs marked coroutines; it does not make unsupported unittest class structures compatible.
How pytest-asyncio processes an async test function
The execution path begins when pytest discovers pytest-asyncio as a plugin. The project metadata declares a `pytest11` entry point named `asyncio`, although the available configuration excerpt does not show the complete registration expression. The README states that installing the package is sufficient for pytest to pick it up.
Once a test is marked with `@pytest.mark.asyncio`, pytest can treat the coroutine function as a test item and execute its body asynchronously. The README's example makes the intended data flow straightforward. pytest collects `test_some_asyncio_code`, the plugin supplies support for running the coroutine, and the test reaches its first suspension point when it evaluates `await library.do_something()`. Once that operation completes, its return value is assigned to `res`, and the assertion compares it with `b"expected result"`.
The important distinction is between coroutine creation and coroutine execution. Declaring `async def` produces a coroutine when called, but the README's documented support for coroutine test functions depends on pytest and the plugin recognizing the marked test. Users should therefore retain the marker on coroutine tests unless the full documentation specifies another supported configuration.
The package metadata also places the plugin within a constrained dependency graph. It declares `pytest>=8.4,<10`, requires Python `>=3.10`, and installs `backports-asyncio-runner>=1.1,<2` only when `python_version<'3.11'`. This is more controlled than assuming compatibility with every Python and pytest combination.
The README does not describe internal classes, event-loop policy selection, or the complete implementation of plugin registration. Those implementation details should come from the project source or its full documentation rather than an inferred account of how every suspension point is scheduled.
Installing pytest-asyncio and writing the first coroutine test
The README gives installation as a single pip command. In an existing Python project, install the package into the environment used to run the test suite. The documentation states that this is enough for pytest to discover pytest-asyncio.
$ pip install pytest-asyncioAfter installation, create or edit a pytest test module using the plugin's marker and an awaited operation. The following example is reproduced from the README and is the smallest complete test shape documented there:
@pytest.mark.asyncio
async def test_some_asyncio_code():
res = await library.do_something()
assert b"expected result" == resReplace `library.do_something()` with the async operation being tested, then execute the test through the project's pytest setup. A successful run reaches the assertion after `do_something()` returns. If that operation cannot produce the expected bytes value, pytest reports an assertion failure. If the coroutine is not arranged as a supported pytest-asyncio test, collection or execution may fail before the intended assertion is reached.
The repository Makefile defines a contributor test target that starts pytest through coverage:
coverage run -m pytestThat target is not the introductory installation command. Its dependencies are listed under the `testing` extra in `pyproject.toml` as `coverage>=6.2` and `hypothesis>=5.7.1`. The README also states that tests can be run with `tox`, and asks contributors to keep tests passing and coverage from decreasing before marking a pull request ready for review.
Python and pytest constraints in the 1.4.0 metadata
The project metadata narrows adoption in ways that are easy to overlook. `requires-python` is set to `>=3.10`, and classifiers cover Python 3.10, 3.11, 3.12, 3.13, and 3.14. The package is also classified as Python 3 only. An environment still running Python 3.9 or earlier falls outside the declared requirement.
The pytest dependency is `pytest>=8.4,<10`. This lower bound matters when an existing suite runs with an older pytest, while the upper bound excludes pytest 10 and later. Migrating an older suite may therefore require upgrading pytest as well as installing pytest-asyncio. The README does not document the compatibility steps for that migration.
Two dependencies are conditional. Python versions below 3.11 receive `backports-asyncio-runner>=1.1,<2`, and Python versions below 3.13 receive `typing-extensions>=4.12`. A project maintaining several Python environments should inspect its resolved dependency set because installation across supported interpreter versions does not produce an identical package list.
The release history lists `v1.4.0` on 2026-05-26. It also lists `v1.4.0a2` on 2026-05-15 and `v1.4.0a1` on 2026-04-15. The repository is not archived, and its last push was on 2026-10-16. `pyproject.toml` classifies the distribution as `Development Status:: 5 - Production/Stable`, but that classifier is a project declaration rather than a guarantee about every environment or release upgrade.
Dependency pinning remains important for organizations that deploy the same suite across Python versions. The declared constraints should be checked before changing the interpreter or pytest, because installation can fail or resolve to different conditional dependencies after those changes.
Where pytest-asyncio is the wrong test integration choice
The clearest limitation is ordinary `unittest` inheritance. The README explicitly says that test classes subclassing the standard `unittest` library are not supported. It recommends `unittest.IsolatedAsyncioTestCase`, or an async framework such as `asynctest`. A team should not choose pytest-asyncio merely because its project uses asyncio if its tests are defined as subclasses of `unittest.TestCase`.
There is also a documentation boundary around event loops and asynchronous fixtures. The README establishes support for coroutine test functions, but it does not document loop policies, fixture lifecycle rules, timeout handling, session-level async behavior, or custom event-loop configuration. Those details should not be inferred from the existence of the plugin. The full documentation at `https://pytest-asyncio.readthedocs.io` is the relevant place to verify them before designing a more elaborate test architecture.
The plugin is also not a replacement for the code under test. It lets a pytest coroutine await that code, but it does not remove the need for correct cleanup, deterministic assertions, or appropriate handling of pending asynchronous operations. The README example demonstrates invocation and assertion only. It does not claim to diagnose failures inside the awaited library call.
For projects that standardize on pytest-style coroutine tests, the fit is direct. For `unittest.TestCase` suites or teams that need loop-management behavior not documented by the README, choosing an integration before checking their full test structure could create unnecessary migration work.
pytest-asyncio compared with IsolatedAsyncioTestCase
The concrete alternative named by the README is `unittest.IsolatedAsyncioTestCase`. The difference is the testing framework and class model. pytest-asyncio is a pytest plugin that supports coroutine test functions through pytest collection and execution. `IsolatedAsyncioTestCase` belongs to the standard `unittest` library and provides asynchronous unittest support for a class-based test model.
That distinction determines where each tool fits. pytest-asyncio is appropriate for suites already built around pytest functions and the `@pytest.mark.asyncio` marker. `IsolatedAsyncioTestCase` is the README's stated option when the required test class inherits from the standard unittest class. The latter provides an asyncio-compatible path for a structure pytest-asyncio explicitly does not support.
The README also names `asynctest` as an async framework, but it does not document installation, configuration, or compatibility for that option. A detailed comparison with `asynctest` would therefore go beyond the repository documentation. The traceable comparison is narrower: pytest-asyncio targets pytest coroutine functions, while `IsolatedAsyncioTestCase` targets unittest classes.
A project should base its decision on its existing test framework rather than on language features alone. Code built with `asyncio` can be tested through different integrations, but pytest-asyncio's documented shape does not include ordinary `unittest.TestCase` subclasses. Moving an existing class-based unittest suite to marked pytest functions is a structural change, not merely adding a package to the environment.
License, maintenance, and upgrade work for pytest-asyncio
pytest-asyncio is distributed under `Apache-2.0`. The repository contains a `LICENSE` file, and `pyproject.toml` identifies that license and the license file in its package metadata. Teams that redistribute the package or incorporate it into a managed distribution process should review the actual `LICENSE` text for applicable attribution and notice obligations. This identifies the declared license without offering legal advice.
The repository is not archived, and its last push was on 2026-10-16. The stable `v1.4.0` release is dated 2026-05-26. Those facts establish recent repository activity, but they do not remove upgrade work. The metadata constrains both Python and pytest versions, so a future environment change may require resolving a new pytest-asyncio version or changing the test setup.
Contributor maintenance has explicit project-level requirements. The README says tests can be run with `tox` and asks contributors to keep tests passing and coverage from decreasing before marking a pull request ready for review. The repository includes `tox.ini`, `uv.lock`, `tests/`, and `tools/`, but the README does not enumerate every tox environment or describe a general upgrade procedure.
For ordinary users, installation requires adding the package to the appropriate Python environment. For organizations pinning dependencies, the recurring cost is checking each pytest and Python upgrade against the declared bounds. `pyproject.toml` is the concrete file to inspect first for those constraints before changing the test environment.
Editorial conclusion
pytest-asyncio fits Python projects that test asyncio coroutines with pytest and use a supported interpreter and pytest release. It is not suitable for standard unittest.TestCase subclasses. Before adoption, verify the Python 3.10 requirement, the pytest>=8.4,<10 dependency, and whether the test suite needs asynchronous fixture or event-loop behavior beyond the README's coroutine example.
Frequently asked questions
How do I install pytest-asyncio?
Run `pip install pytest-asyncio`. The README states that this is sufficient for pytest to pick up the plugin.
Which Python and pytest versions does pytest-asyncio support?
The project metadata requires Python `>=3.10` and declares `pytest>=8.4,<10`. Its classifiers cover Python 3.10 through Python 3.14.
Can pytest-asyncio run async tests in unittest.TestCase subclasses?
No. The README excludes test classes that subclass the standard `unittest` library. It points users to `unittest.IsolatedAsyncioTestCase` or an async framework such as `asynctest`.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/pytest-dev-pytest-asyncio)Was this useful?