Skip to main content

Pytest plugin for regression testing via branch comparison

Project description

pytest-drift

A pytest plugin for regression testing via branch comparison. When a test returns a value, the plugin runs the same test on a base git branch and compares the results — catching regressions before they merge.

How it works

  1. You run pytest --drift BASE_BRANCH
  2. For every test that returns a non-None value, the plugin:
    • Records the return value from the current branch (HEAD)
    • Simultaneously runs the same tests on BASE_BRANCH in a git worktree
    • Compares the two results at the end of the session
  3. Tests returning None (the default for normal pytest tests) are ignored entirely

The base branch runs in parallel with your HEAD tests, so total wall time is approximately max(HEAD_time, BASE_time) rather than HEAD_time + BASE_time.

Installation

pip install pytest-drift

# With smart DataFrame diff reports (recommended):
pip install "pytest-drift[datacompy]"

Usage

CLI flag

pytest --drift main
pytest --drift origin/main

Environment variable

export PYTEST_DRIFT_BASE_BRANCH=main
pytest

Writing regression tests

Return a value from your test — that's it:

def test_revenue_calculation():
    df = compute_revenue(load_data())
    return df  # compared against the same function on BASE_BRANCH

def test_model_accuracy():
    return evaluate_model()  # compared as a float

def test_pipeline_output():
    return run_pipeline()  # compared as a dict, list, DataFrame, etc.

Normal tests (returning None) are unaffected and run as usual.

Comparison logic

The plugin dispatches comparison based on the return type:

Type Comparison method
pd.DataFrame Auto-detects join columns; uses datacompy if installed, else pd.testing.assert_frame_equal
pd.Series Converted to DataFrame, same path as above
float / np.floating math.isclose with rtol=1e-5, atol=1e-8
np.ndarray np.testing.assert_array_almost_equal (5 decimal places)
dict Recursive key-by-key comparison
list / tuple Element-wise comparison
Everything else ==, with repr() diff on failure

Pandas index auto-detection

When comparing DataFrames, the plugin automatically finds the best join key:

  1. Named index: if the DataFrame already has a named (non-RangeIndex) index, it's used directly
  2. MultiIndex: all named index levels are used
  3. Column heuristic: searches combinations of up to 3 non-float columns with full cardinality (every row is unique in that combination)
  4. Positional fallback: if no unique key is found, rows are compared positionally

You can also pass join_columns explicitly by calling compare_dataframes directly from pandas_utils.

Terminal output

At the end of the session a regression summary is printed:

========================================================================
REGRESSION COMPARISON SUMMARY
========================================================================
PASSED tests/test_revenue.py::test_revenue_calculation
FAILED tests/test_model.py::test_model_accuracy
    Float mismatch:
      head: 0.923
      base: 0.941
------------------------------------------------------------------------
1 passed, 1 failed (2 total regression comparisons)

How branch switching works

The plugin uses git worktree add to check out BASE_BRANCH into a temporary directory — your working tree is never touched. The worktree is cleaned up automatically after the session.

HEAD tests run         ─────────────────────────▶  sessionfinish
                                                       │
git worktree add ──▶  BASE tests run in parallel  ────┘  compare

Requirements

Package Required Purpose
pytest >= 7.0 Yes Core
cloudpickle >= 3.0 Yes Serialization of return values
pandas >= 1.5 Yes DataFrame/Series support
datacompy >= 0.9 Optional Rich DataFrame diff reports
pyarrow >= 10.0 Optional Parquet storage for large DataFrames

Caveats

  • The base branch subprocess uses the same Python environment as HEAD — if your project uses tox or nox, point to the correct environment
  • Session-scoped fixtures with side effects (e.g. starting a server) will run twice — once per session
  • Tests that fail on HEAD are not compared (no base result is fetched for them)
  • Tests that fail on BASE produce a "base branch test failed, cannot compare" warning

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pytest_drift-0.1.1.tar.gz (17.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pytest_drift-0.1.1-py3-none-any.whl (14.8 kB view details)

Uploaded Python 3

File details

Details for the file pytest_drift-0.1.1.tar.gz.

File metadata

  • Download URL: pytest_drift-0.1.1.tar.gz
  • Upload date:
  • Size: 17.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pytest_drift-0.1.1.tar.gz
Algorithm Hash digest
SHA256 13914335580b8185095d3737afb3df6d4a20aae687e9e913f4efcaa968a40f5a
MD5 5669670200661f7c6265e45522a0bdd7
BLAKE2b-256 661dbb59fee2a088089ee46016b0a434c41e8b2309cb4aa65f0e37f460dd6f3b

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_drift-0.1.1.tar.gz:

Publisher: python-publish.yml on jackxxu/pytest-drift

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pytest_drift-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: pytest_drift-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 14.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pytest_drift-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 041f8656f514455adf7aa85fb36daceba75ccf8753efa294a8f6955bba1b0058
MD5 2ca4c40da7f95f63838c25eb59a85ada
BLAKE2b-256 c08d98445ff0eed7d570d4d0252d50d413bd4e621ee1fa4faed93e1dedd06892

See more details on using hashes here.

Provenance

The following attestation bundles were made for pytest_drift-0.1.1-py3-none-any.whl:

Publisher: python-publish.yml on jackxxu/pytest-drift

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page