How to speed up pytest

Runners

May 20, 2025

How to Run Pytest Faster: A Guide to Turbocharging Your Test Suite

If you’re a Python developer using pytest, you’ve probably stared at your terminal, coffee in hand, willing those tests to finish faster. Slow test suites aren’t just annoying; they can grind your team’s momentum to a halt, delay releases, and make everyone grumpy. Whether you’re a solo coder or part of a sprawling CI/CD pipeline running thousands of tests, this guide will help you speed up pytest, squash flaky tests, and maybe even save some CI budget. Let’s make your tests zoom without sacrificing reliability or coverage.

Why Test Speed Is a Big Deal

Before we jump into the how-to, let’s talk about why pytest speed matters—especially if you’re building developer tools or working in a fast-moving SaaS environment:

  • Developer sanity: Slow tests kill your flow state. Every second you’re not waiting adds up to more productive coding time.

  • Faster CI pipelines: Quick tests mean faster feedback, fewer bugs slipping through, and lower cloud compute bills.

  • Happier teams: Nobody likes twiddling their thumbs while tests chug along. Fast tests keep engineers smiling.

  • More throughput: Speedy pipelines let you merge more PRs daily, keeping your project moving at lightning speed.

Ready to make your tests fly? Here’s how.

1. Profile Your Tests to Find the Slowpokes

You can’t fix what you don’t measure. Start by figuring out what’s dragging your suite down:

  • Which tests are the slowest?

  • Is setup or teardown eating up time?

  • Are you running tests you don’t need?

Pytest’s --durations flag is your friend:

pytest --durations=10

This spits out the 10 slowest tests, often revealing low-hanging fruit for optimization.

Want to go deeper? Install pytest-profiling for detailed cProfile reports:

pip install pytest-profiling
pytest --profile

If you’re running tests in CI, pipe this data to a test analytics tool to spot trends or regressions over time. Knowledge is power!

2. Parallelize with pytest-xdist for Instant Gains

Want a quick win? Parallelize your tests with pytest-xdist. It’s like giving your test suite a shot of espresso.

Install it:

Then run:

pytest -n

This uses all available CPU cores to run tests concurrently. Want more control? Specify the number of workers:

pytest -n 4  # runs 4 workers

A few gotchas:

  • Tests need to be independent. Shared resources (like temp files or databases) can cause flakiness.

  • You might need to refactor stateful fixtures to avoid conflicts.

3. Save Time on Database Setup (Django Users)

If you’re using Django, your database is probably the biggest bottleneck. Creating a fresh DB for every test run is a time suck.

Use pytest-django’s --reuse-db flag to skip recreating the database:

pytest --reuse-db

This can shave minutes off large Django projects. Just make sure your tests don’t depend on a pristine DB unless absolutely necessary.

4. Cut Down on Database Hits

Database calls are sloooow. Every ORM query or TestCase that touches the DB adds up.

Try these:

  • Use @pytest.mark.django_db only for tests that truly need the database.

  • For pure logic tests, stick to unittest.TestCase or plain pytest functions.

  • Mock slow I/O with unittest.mock or pytest-mock.

Shifting even 20% of your tests away from the DB can make a noticeable difference.

5. Break Up Long Tests and Optimize Fixtures

Got a test that feels like it’s running a marathon? Split it into smaller, focused tests. They’re faster and easier to debug.

Also, take a hard look at your fixtures. For expensive setup/teardown, use broader fixture scopes:

@pytest.fixture(scope="session")
def expensive_setup():
    # Runs once per session, not per test
    ...

But don’t go overboard—too broad a scope can lead to flaky tests or weird side effects.

6. Cache Results to Skip Redundant Work

Pytest’s built-in caching can save you from rerunning tests unnecessarily:

pytest --lf  # Runs only tests that failed last time

Or:

pytest --last-failed

In CI, cache .pytest_cache between runs, but be careful to avoid stale results.

7. Run Only What You Need

Why run your entire test suite when you’re just tweaking one feature? Scope your tests with --maxfail or -k:

pytest -k "Login and not slow"

For CI, tools like pytest-testmon (which tracks test dependencies) or tox with matrix builds can run only the relevant subset. You can even build custom test selection based on git diffs.

8. Skip Slow Tests for Quick Feedback

Some tests are just slow by design—think big data imports or complex integrations. Mark them:

@pytest.mark.slow
def test_big_data_import():
    ...

Then skip them for daily runs:

pytest -m "not slow"

Save these for nightly or weekly builds to balance speed and coverage.

9. Slim Down Imports

Heavy imports can quietly slow your tests. That massive pandas or TensorFlow import might be overkill.

Tips:

  • Move heavy imports inside test functions.

  • Avoid circular or duplicate imports.

  • Use tools like import-tracker or pyinstrument to profile import times.

10. Track Performance with Test Analytics

Speed isn’t just about running tests—it’s about keeping them fast over time. Use analytics platforms like BuildPulse, TestRail, Allure, or CircleCI Test Insights to monitor:

  • Test runtime trends

  • Flaky tests

  • Failure patterns

Set alerts for when test durations creep up. It’s like a fitness tracker for your test suite.

11. Optimize CI with Containers

Your local tests might be snappy, but CI can still feel like molasses. Fix it:

  • Use prebuilt Docker images with Python, dependencies, and DBs ready to go.

  • Use RAM disks for ephemeral DBs to speed up I/O.

  • Run CI jobs on beefier machines.

  • Shard tests with GitHub Actions, CircleCI, or Buildkite matrix builds.

Here’s a sample Docker base:

FROM python:3.11-slim

RUN pip install pytest pytest-xdist pytest-django

CMD ["pytest", "-n", "auto"]

Combine this with caching for .tox, .venv, or .pytest_cache.

12. Squash Flaky Tests

Flaky tests are the worst—they waste time and make you question your automation. Root them out:

  • Ditch sleep statements.

  • Mock network calls and I/O.

  • Use @pytest.mark.flaky sparingly as a last resort.

Tools like BuildPulse or the flaky pytest plugin can help detect and manage flakiness.

13. Bonus: Combine Assertions with pytest-check

Pytest stops at the first failed assertion, which can mean multiple runs to debug. Use pytest-check to run all assertions and report every failure:

import pytest_check as check

def test_something():
    check.equal(a, 1)
    check.is_in(b, [2, 3])

This saves time during debugging and cuts down on reruns.

Wrapping up

Speeding up pytest isn’t just about cutting seconds—it’s about supercharging your entire development cycle. For high-growth SaaS teams or anyone building developer tools, fast tests mean:

  • More features shipped

  • Quicker bug fixes

  • Happier developers

  • Leaner infrastructure costs

All of these changes can speed up your tests, but at a certain point, your hardware and execution environment play a role as well. BuildPulse Runners can help by running your GitHub Actions jobs 2x faster, at half cost.

FAQ

Does BuildPulse replace my current CI system?

No.

We use GitHub Actions / CircleCI / Semaphore CI self-hosted functionality to run your builds on our infrastructure.

Other than faster builds, there are no changes to tooling or your developer workflows. You can continue using your CI system as-is.

How is BuildPulse faster than GitHub Actions hosted runners?

We use GitHub’s self-hosted functionality to run your builds on our infrastructure with latest generation + high single-core performance CPUs, also then further optimized for CI-type workloads. We’ve also tuned our VMs and block storage devices, increasing baseline performance while also cutting costs in half.

We also provide a toolkit to further speed up your pipelines, which includes ultra fast remote docker builders, docker layer caching, dependency caching, and more. With all of these improvements, we’ve seen 2x+ performance improvements in build times.

Can I use BuildPulse with other CI providers than GitHub Actions?

Yes! BuildPulse Runners will run jobs for CircleCI, SemaphoreCI - GitLab coming soon.

We aim to support all popular CI systems. If you're using one that's not listed, please contact support@buildpulse.io!

Is there a free trial available?

Yes, you can book a meeting here!

How do you secure my builds?

BuildPulse runs each job in a network- and compute- isolated environment with ephemeral VMs that leave behind a clean state after every run.

Do you support Mac and Windows runners?

This is on our roadmap! Email us at hello@buildpulse.io, or book a demo here!

Is BuildPulse SOC 2 compliant?

Yes, BuildPulse is SOC 2 Type 2 compliant.

Contact us at hello@buildpulse.io for more information.

How are BuildPulse Runners priced?

BuildPulse Runners charges on a per-second basis, which depend on the runner-type used. See our pricing page for more details.

How long does implementation/integration with BuildPulse take?

The minimum implementation involves 2 steps: Signing up for BuildPulse, and changing 1 in your GitHub Actions yaml file.

If you're using Semaphore CI or Circle CI, it's a 4 line change. See our Quickstart guide for more details.

Does BuildPulse replace my current CI system?

No.

We use GitHub Actions / CircleCI / Semaphore CI self-hosted functionality to run your builds on our infrastructure.

Other than faster builds, there are no changes to tooling or your developer workflows. You can continue using your CI system as-is.

How is BuildPulse faster than GitHub Actions hosted runners?

We use GitHub’s self-hosted functionality to run your builds on our infrastructure with latest generation + high single-core performance CPUs, also then further optimized for CI-type workloads. We’ve also tuned our VMs and block storage devices, increasing baseline performance while also cutting costs in half.

We also provide a toolkit to further speed up your pipelines, which includes ultra fast remote docker builders, docker layer caching, dependency caching, and more. With all of these improvements, we’ve seen 2x+ performance improvements in build times.

Can I use BuildPulse with other CI providers than GitHub Actions?

Yes! BuildPulse Runners will run jobs for CircleCI, SemaphoreCI - GitLab coming soon.

We aim to support all popular CI systems. If you're using one that's not listed, please contact support@buildpulse.io!

Is there a free trial available?

Yes, you can book a meeting here!

How do you secure my builds?

BuildPulse runs each job in a network- and compute- isolated environment with ephemeral VMs that leave behind a clean state after every run.

Do you support Mac and Windows runners?

This is on our roadmap! Email us at hello@buildpulse.io, or book a demo here!

Is BuildPulse SOC 2 compliant?

Yes, BuildPulse is SOC 2 Type 2 compliant.

Contact us at hello@buildpulse.io for more information.

How are BuildPulse Runners priced?

BuildPulse Runners charges on a per-second basis, which depend on the runner-type used. See our pricing page for more details.

How long does implementation/integration with BuildPulse take?

The minimum implementation involves 2 steps: Signing up for BuildPulse, and changing 1 in your GitHub Actions yaml file.

If you're using Semaphore CI or Circle CI, it's a 4 line change. See our Quickstart guide for more details.

Does BuildPulse replace my current CI system?

No.

We use GitHub Actions / CircleCI / Semaphore CI self-hosted functionality to run your builds on our infrastructure.

Other than faster builds, there are no changes to tooling or your developer workflows. You can continue using your CI system as-is.

How is BuildPulse faster than GitHub Actions hosted runners?

We use GitHub’s self-hosted functionality to run your builds on our infrastructure with latest generation + high single-core performance CPUs, also then further optimized for CI-type workloads. We’ve also tuned our VMs and block storage devices, increasing baseline performance while also cutting costs in half.

We also provide a toolkit to further speed up your pipelines, which includes ultra fast remote docker builders, docker layer caching, dependency caching, and more. With all of these improvements, we’ve seen 2x+ performance improvements in build times.

Can I use BuildPulse with other CI providers than GitHub Actions?

Yes! BuildPulse Runners will run jobs for CircleCI, SemaphoreCI - GitLab coming soon.

We aim to support all popular CI systems. If you're using one that's not listed, please contact support@buildpulse.io!

Is there a free trial available?

Yes, you can book a meeting here!

How do you secure my builds?

BuildPulse runs each job in a network- and compute- isolated environment with ephemeral VMs that leave behind a clean state after every run.

Do you support Mac and Windows runners?

This is on our roadmap! Email us at hello@buildpulse.io, or book a demo here!

Is BuildPulse SOC 2 compliant?

Yes, BuildPulse is SOC 2 Type 2 compliant.

Contact us at hello@buildpulse.io for more information.

How are BuildPulse Runners priced?

BuildPulse Runners charges on a per-second basis, which depend on the runner-type used. See our pricing page for more details.

How long does implementation/integration with BuildPulse take?

The minimum implementation involves 2 steps: Signing up for BuildPulse, and changing 1 in your GitHub Actions yaml file.

If you're using Semaphore CI or Circle CI, it's a 4 line change. See our Quickstart guide for more details.

Ready for Takeoff?

Ready for Takeoff?

Ready for Takeoff?