Find slow tests with pytest durations

Measure setup, call, and teardown separately, then improve the expensive work without weakening isolation or confusing parallel throughput with individual latency.

Best practices
Illustration of software components, data, and application workflows

Use pytest's built-in --durations=N option to list the slowest test phases. The report distinguishes setup, the test call, and teardown, so an expensive fixture does not automatically look like slow test logic.

Start with the duration report

python -m pytest --durations=10
python -m pytest --durations=1
python -m pytest --durations=0 --durations-min=0

0 removes the limit on the number of entries. The separate minimum-duration setting still matters, which is why the final command explicitly sets both options.

Illustrative output might look like this; these numbers are not benchmark results:

3.42s call     tests/test_api.py::test_full_sync
1.15s setup    tests/test_db.py::test_migration
0.87s call     tests/test_parser.py::test_large_file

Setup and teardown include fixture and other phase work. A session fixture's setup is charged to the test that first requests it, so inspect fixture dependencies before blaming that individual test.

To focus on phases lasting at least half a second:

python -m pytest --durations=0 --durations-min=0.5

Optimize the measured bottleneck

If repeated setup dominates, consider a module- or session-scoped fixture and reset mutable state between tests. Sharing an expensive resource is useful only when it preserves isolation.

For unit tests, replace irrelevant external I/O with a controlled fake or mock; retain dedicated integration coverage for behavior that depends on the real service. Tools such as pytest-mock and responses can help with suitable applications, but changing a test boundary changes what it proves.

For independent tests, pytest-xdist can improve throughput:

python -m pip install pytest-xdist
python -m pytest -n auto

Worker startup, scheduling, memory use, and contention limit the benefit. Parallel execution is not a linear speedup guarantee, and it does not explain why one operation is slow. Profile that operation separately when the duration report has narrowed the search.

Register a slow marker if you maintain a faster subset:

[pytest]
markers =
    slow: exercises an intentionally expensive path

Run it with python -m pytest -m "not slow", while keeping the excluded coverage in another required job. Splitting a large test or parametrizing it can improve diagnosis; neither inherently reduces total work.

Keep comparable CI measurements

Store a bounded duration report and compare runs with similar machines, dependencies, and workload. A total-time budget must be enforced by the CI system or another explicit mechanism; --durations reports timing without imposing a deadline.

FAQ

Is 0 equivalent to a very large entry limit?

Only if the finite limit exceeds every reported phase. Both remain subject to the minimum threshold.

Can I export timing data?

Use python -m pytest --junitxml=report.xml. By default, testcase time includes setup and teardown as well as the call; the junit_duration_report setting can change that. Structured-report plugins offer other formats when needed.

Why do timings vary?

Caches, machine load, scheduling, and external services all contribute. Repeat comparable runs and inspect the distribution. Hiding small values with a threshold does not remove measurement noise.

References: Running pytest, pytest output and duration reports, pytest-xdist scheduling.

Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting