Skip to main content

pytest-gitscope

Smart test filtering based on Git revisions

pytest-gitscope is a pragmatic pytest plugin that runs only the tests that matter. By analyzing your Git history, it intelligently determines which tests are affected by your changes, helping you ship faster.

Features

Testing everything on every change is thorough but impractical. pytest-gitscope follows the principle of "test what changed" – giving you confidence in your modifications while respecting your time.

  • 🎯 Targeted Testing - Run only tests related to modified files
  • ⚡ Fast Execution - Dramatically reduce test execution time
  • 🔍 Git Integration - Seamlessly works with your Git workflow
  • 📊 Smart Detection - Automatically detects affected test files and dependencies
  • 🛠️ Flexible Filtering - Support for commit ranges, branches, and specific revisions

Quick Start

pip install pytest-gitscope

# Run tests affected by changes in the last commit
pytest --gitscope HEAD~1

# Run tests affected by changes between two commits
pytest --gitscope main..feature-branch

Perfect for CI/CD pipelines and local development to focus on what matters most.

Understanding pytest-gitscope with a complete example

Let's walk through a real-world example to see exactly how pytest-gitscope works.

Initial Project Structure

myproject/
├── src/
│   ├── calculator.py
│   ├── user_manager.py
│   └── utils.py
├── tests/
│   ├── test_calculator.py
│   ├── test_user_manager.py
│   ├── test_utils.py
│   └── test_integration.py
└── docs/
    └── README.md

Step 1: Initial State

Your project has 15 tests total:

$ pytest --collect-only -q
15 tests collected

Running all tests takes 45 seconds:

$ pytest
================ 15 passed in 45.23s ================

Step 2: Making Changes

You're working on a new feature and modify two files:

Changes to src/calculator.py:

def add(a, b):
    return a + b

def multiply(a, b):  # ← NEW FUNCTION
    return a * b

Changes to docs/README.md:

# MyProject

Updated documentation with new examples...

Step 3: See What Git Detects

$ git diff --name-only HEAD~1
src/calculator.py
docs/README.md

Step 4: Run pytest-gitscope

Instead of running all 15 tests, use pytest-gitscope:

$ pytest --gitscope HEAD~1

What happens internally:

  1. File Analysis: pytest-gitscope runs git diff --name-only HEAD~1

    Changed files: ['src/calculator.py', 'docs/README.md']
    
  2. Test Mapping: It identifies which tests are affected:

    src/calculator.py → tests/test_calculator.py
    docs/README.md → (no tests affected)
    
  3. Dependency Detection: It scans for imports and finds:

    tests/test_integration.py imports calculator.py
    
  4. Final Test Selection:

    Selected tests:
    ✓ tests/test_calculator.py (directly affected)
    ✓ tests/test_integration.py (imports calculator.py)
    ✗ tests/test_user_manager.py (not affected)
    ✗ tests/test_utils.py (not affected)
    

Output:

$ pytest --gitscope HEAD~1
========================== test session starts ==========================
gitscope: Analyzing changes from HEAD~1
collected 15 items / 7 deselected / 8 selected
Some tests have been deselected by pytest-gitscope plugin, because they have not been affected by the changes from HEAD~1

tests/test_calculator.py ✓✓✓✓✓
tests/test_integration.py ✓✓✓

=================== 8 passed, 7 deselected in 12.34s ====================

Step 5: Compare the Results

Method Tests Run Time
Regular pytest 15 tests 45.23s
pytest --gitscope 8 tests 12.34s
Savings 7 tests skipped 73% faster

Real-World Scenarios

Scenario 1: Feature Branch

# Test only changes in your feature branch
$ pytest --gitscope main..feature/new-auth
gitscope: Selected 12 tests from 4 test files (28 tests skipped)

Scenario 2: Last 3 Commits

# Test changes from last 3 commits
$ pytest --gitscope HEAD~3
gitscope: Selected 6 tests from 2 test files (34 tests skipped)

Scenario 3: Specific Files Only

# Test only your calculator changes
$ pytest --gitscope HEAD~1 tests/test_calculator.py
gitscope: Selected 5 tests from 1 test file (35 tests skipped)

What Gets Detected

✅ Direct matches: src/calculator.py → tests/test_calculator.py
✅ Import dependencies: Files that import changed modules
✅ Test utilities: Shared test fixtures and utilities
✅ Configuration changes: pytest.ini, conftest.py

❌ Documentation only: README.md, *.md files (configurable)
❌ Unrelated modules: Files with no test connections

Important Note: Dependency Management

⚠️ pytest-gitscope works best with projects using package managers like Poetry or uv.

Without a proper dependency manager, pytest-gitscope might miss some test dependencies. Here's why:

With Poetry/uv (Recommended):

# pytest-gitscope can analyze pyproject.toml and lock files
# to understand the full dependency graph
src/calculator.py → tests/test_calculator.py ✓
src/calculator.py → tests/test_math_integration.py ✓ (detected via deps)

Without package manager:

# pytest-gitscope relies only on direct imports scanning
src/calculator.py → tests/test_calculator.py ✓
src/calculator.py → tests/test_math_integration.py ? (might be missed)

Recommendation: For best results, use pytest-gitscope in projects with:

  • pyproject.toml (Poetry, uv, setuptools)
  • requirements.txt with proper dependency tracking
  • Clear import patterns

Integration with CI/CD

Perfect for speeding up your CI pipeline:

# .github/workflows/test.yml
- name: Run targeted tests
  run: pytest --gitscope origin/main..HEAD

This way, pull requests only run tests affected by the changes, dramatically reducing build times while maintaining confidence in your code quality.

Cookbook

Use gitscope on gitlab-ci

Because gitlab-ci fetch the latest commit on detached mode, you will need to fetch the target branch as a prerequisite.

Job:
  before_script:
    - git fetch --depth=1 origin ${CI_MERGE_REQUEST_TARGET_BRANCH_NAME:-$CI_DEFAULT_BRANCH}
  script:
    - pytest --gitscope origin/${CI_MERGE_REQUEST_TARGET_BRANCH_NAME:-$CI_DEFAULT_BRANCH}

Disable short-circuiting on git push with gitlab-ci

Sometimes you want to disable short-circuiting because updating your pyproject.toml file does not change dependencies.

Gitlab allows to provide CI/CD variables to the CI/CD pipeline, if one is created due to the push. Passes variables only to branch pipelines and not merge request pipelines.

git push -o ci.variable="PYTEST_ADDOPTS=--gitscope-no-short-circuits"

Always include tests that depends on a module

The option --gitscope-include-module let you include tests that depends on this module — and its submodules too, due to the way modules are imported in python.

pytest --gitscope-include-module x.y.z tests/

Metadata

Release files for pytest-gitscope 0.7.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pytest-gitscope 0.7.1
File Size Uploaded
pytest_gitscope-0.7.1.tar.gz 16.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pytest-gitscope 0.7.1
File Interpreter ABI Platform
pytest_gitscope-0.7.1-py3-none-any.whl Python 3 none any Details

Total release size: 27.2 kB

Release files / pytest_gitscope-0.7.1.tar.gz

Download URL pytest_gitscope-0.7.1.tar.gz
Size 16.9 kB
Tags Source
SHA-256 checksum
How to use checksums
b1de757eeafcc0e6c6ad0074ec135b9f87bcb9c2306455211ef58fc3cb512b0d
BLAKE2b-256 checksum
How to use checksums
32693fbcbe1827970f6a494ad227d984685ff4ca850c66be7642f628bad4bc5f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.8.18

Release files / pytest_gitscope-0.7.1-py3-none-any.whl

Download URL pytest_gitscope-0.7.1-py3-none-any.whl
Size 10.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3a035e3c719182e942371a274dcf135378217c293f9183f295d4b60fdfac8f3d
BLAKE2b-256 checksum
How to use checksums
da78c4ba377e25ee7b7870ad91f3684b715e9bae2242637b1580d582f2fc3fba
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.8.18

Release history Release notifications | RSS feed

This release

0.7.1 This release

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.3

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page