1. Resources
  2. /
  3. Examples
  4. /
  5. Pytest Parallel

pytest-parallel-example

This repository is an example Buildkite pipeline that runs a Python integration test suite using pytest configured to use paralellism.

See the full Getting Started Guide for step-by-step instructions on how to get this running.

Screenshot of pytest parallel example with 20 steps each running a chunk of the 100 tests

How does it work

This repo has a little python project with 100 integration tests, which currently just sleep for a random amount of time then pass 99% of the time. We’re using uv to run Python.

We start by using a single command step to run pytest:

# .buildkite/pipeline.yml
steps:
  - command: uv run pytest

Then we add parallelism set to 20 so it turns into 20 jobs across 20 agents:

steps:
  - command: uv run pytest
    parallelism: 20

But that runs every test in every job, so we use pytest-split, $BUILDKITE_PARALLEL_JOB and $BUILDKITE_PARALLEL_JOB_COUNT to make each job run a different chunk of the tests:

steps:
  - command: |
      uv run pytest \
        --splits "$${BUILDKITE_PARALLEL_JOB_COUNT}" \
        --group "$$(($${BUILDKITE_PARALLEL_JOB} + 1))"
    parallelism: 20

Note we have to escape the $ because pipeline uploads perform interpolation at upload time and we want to take the variables within each the job instead.

We also do some math because $BUILDKITE_PARALLEL_JOB starts at 0 but pytest-split expects --group to start at 1.

Finally, because this is an integration test suite, test reliability is not always 100%. So we use pytest-retry to retry failed tests:

steps:
  - command: |
      uv run pytest \
        --splits "$${BUILDKITE_PARALLEL_JOB_COUNT}" \
        --group "$$(($${BUILDKITE_PARALLEL_JOB} + 1))" \
        --retries 2
    parallelism: 20

Next steps could be to extract this into a script file, to make that interpolation less awkward. We can also add a log group to make the log output a little nicer. For example:

# .buildkite/steps/pytest

echo "+++ Running pytest"
uv run pytest \
  --splits "${BUILDKITE_PARALLEL_JOB_COUNT}" \
  --group "$((${BUILDKITE_PARALLEL_JOB} + 1))" \
  --retries 2
steps:
  - command: .buildkite/steps/pytest
    parallelism: 20

Lovely ✨

To go further, we could integrate Buildkite Test Engine which can more intelligently manage the split of our test suite. It is aware of the historical timing of tests so can spread tests across jobs so they take roughly the same amount of time, eliminating long tail jobs, meaning your builds finish sooner. It can also manage tests that are known to be flaky, suppressing failures based on rules and managing team workflows to resolve flakey tests, increasing the reliability of your test suite over time.

More examples

Start turning complexity into an advantage

Create an account to get started for free.

Buildkite Pipelines

Platform

  1. Pipelines
  2. Public pipelines
  3. Test Engine
  4. Package Registries
  5. Mobile Delivery Cloud
  6. Pricing

Hosting options

  1. Self-hosted agents
  2. Mac hosted agents
  3. Linux hosted agents

Resources

  1. Docs
  2. Blog
  3. Changelog
  4. Example pipelines
  5. Plugins
  6. Webinars
  7. Case studies
  8. Events
  9. Migration Services
  10. Comparisons
  11. CI/CD perspectives

Company

  1. About
  2. Careers
  3. Press
  4. Security
  5. Brand assets
  6. Contact

Solutions

  1. Replace Jenkins
  2. Workflows for MLOps
  3. Testing at scale
  4. Monorepo mojo
  5. Bazel orchestration

Legal

  1. Terms of Service
  2. Acceptable Use Policy
  3. Privacy Policy
  4. Subprocessors
  5. Service Level Agreement

Support

  1. System status
  2. Forum
© Buildkite Pty Ltd 2025