Skip to main content

A pragmatic pytest plugin that runs only the tests that matter, and ship faster

Project description

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

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/

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_gitscope-0.4.3.tar.gz (16.1 kB view details)

Uploaded Source

Built Distribution

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

pytest_gitscope-0.4.3-py3-none-any.whl (9.9 kB view details)

Uploaded Python 3

File details

Details for the file pytest_gitscope-0.4.3.tar.gz.

File metadata

  • Download URL: pytest_gitscope-0.4.3.tar.gz
  • Upload date:
  • Size: 16.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.13

File hashes

Hashes for pytest_gitscope-0.4.3.tar.gz
Algorithm Hash digest
SHA256 9600071b4f34afc7de14a61fecb1d7296c7d1cf7cf409b8049c81cb0bccbc24d
MD5 18a19a285b6070716bdec513f9cfd441
BLAKE2b-256 be844fe7846ac8c48cebbe36b9d9447817020a0058bd62e2180b43b77f03a95b

See more details on using hashes here.

File details

Details for the file pytest_gitscope-0.4.3-py3-none-any.whl.

File metadata

File hashes

Hashes for pytest_gitscope-0.4.3-py3-none-any.whl
Algorithm Hash digest
SHA256 ccfa0a626f7a9b6ddaf41ac75824b02374c6ba4e655a3c9fdc3baca4c91b6a4a
MD5 a33dca11f151a29a4971dcfb19f9cbcd
BLAKE2b-256 a96c0574a6fc856f91fd8ad253ee2fa15d0b1d049ce87d404e7dc20b069920c7

See more details on using hashes here.

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