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.6.0.tar.gz (16.3 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.6.0-py3-none-any.whl (10.2 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for pytest_gitscope-0.6.0.tar.gz
Algorithm Hash digest
SHA256 bbfcb9d6476f4e8e2aa3497a873f359b35ae62a281afb1a1f2e47a30ffb0e591
MD5 08e0f3c2b4ae02ca0d89860e2a5269c6
BLAKE2b-256 68e3ebd8913d436747e6059734af2dbdefa085548d7c4daab865f82295f6186b

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pytest_gitscope-0.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 57ad840e0b9a8a1edb2d4952de2fe5cb7d9de64d487526a4e7753eb6f48ac7f9
MD5 6fece2c841cf6e4cd9e81fe7c1ece59d
BLAKE2b-256 9978ffe8fa5ceeeb3328fd251309a1756443584fd6d084384a52aecad6894c2d

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