Plugins: xdist, coverage and asyncio

pytest · Advanced · 12 min

Plugins turn pytest into a full testing platform, and a few of them are in almost every serious project.

Installing a plugin package is usually enough: plugins register through the pytest11 entry point and pytest loads them automatically. pytest --trace-config or the header of the run lists active plugins; -p no:name disables one.

The plugins nearly every project meets:

  • pytest-xdist: pytest -n auto distributes tests across one worker process per CPU. Tests must be independent and must not share files, ports or database names; use worker_id or tmp_path to isolate them. Session fixtures run once per worker.
  • pytest-cov: pytest --cov=mypkg --cov-report=term-missing measures line (and with --cov-branch, branch) coverage and shows uncovered lines. --cov-fail-under=90 enforces a threshold in CI.
  • pytest-asyncio: runs async def tests. Mark them with @pytest.mark.asyncio, or set asyncio_mode = "auto" in configuration to treat every async test and fixture as asyncio. Async fixtures use @pytest_asyncio.fixture in strict mode.
  • pytest-mock (mocker fixture), pytest-randomly (random order, exposes hidden dependencies between tests), pytest-timeout, pytest-django and anyio's plugin for trio/asyncio are other common picks.

Pin plugin versions alongside pytest, and use required_plugins in configuration so a missing plugin fails loudly instead of silently skipping behavior.

Example

# test_async.py   (pip install pytest-asyncio pytest-xdist pytest-cov)
import asyncio
import pytest

async def fetch_price(symbol):
    await asyncio.sleep(0.01)                  # pretend network call
    return {"ACME": 12.5}.get(symbol)

@pytest.mark.asyncio
async def test_fetch_price():
    assert await fetch_price("ACME") == 12.5

@pytest.mark.asyncio
async def test_concurrent_fetches():
    prices = await asyncio.gather(fetch_price("ACME"), fetch_price("NOPE"))
    assert prices == [12.5, None]

# $ pytest -n auto                       # parallel across CPUs
# $ pytest --cov=shop --cov-report=term-missing --cov-fail-under=90

Remember

  • Installed plugins load automatically via entry points
  • pytest-xdist -n auto runs tests in parallel; isolate shared resources
  • pytest-cov measures coverage and can enforce a minimum
  • pytest-asyncio runs async def tests (marker or asyncio_mode=auto)

Watch out for

  • Turning on -n auto with tests that share a fixed database name or temp file and getting random failures.

Check yourself

  1. With pytest-xdist, how often does a session-scoped fixture run?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.pytest.org/en/stable/how-to/plugins.html